Возможно ли превратить этот шаблон HoC в синтаксис React Hook?

#javascript #reactjs #react-hooks

#javascript #reactjs #реагирующие хуки

Вопрос:

Я пытаюсь создать повторно используемый компонент, который изменяет его поведение — по сути, рендеринг либо в SVG, либо в Canvas. В настоящее время я пытаюсь сделать это, обернув его в HoC (однако я пытаюсь использовать React hooks), так что все это выглядит немного плоско.


Редактировать с дополнительной информацией

При текущем подходе (HoC возвращает функцию) Я получаю следующую ошибку:

Функции недопустимы как дочерние элементы React. Это может произойти, если вы возвращаете компонент вместо из render . Или, может быть, вы хотели вызвать эту функцию, а не возвращать ее.

Если я удалю вызов функции в HoC,:

React.jsx: недопустимый тип — ожидал строку (для встроенных компонентов) или класс / функцию (для составных компонентов), но получил: object


Я видел пример преобразования HoC в React hooks, но я изо всех сил пытаюсь понять, как я мог бы преобразовать это — если это вообще возможно, и интересно, может ли кто-нибудь дать мне указатель? Возможно, я попытался спроектировать это неправильно, так как это кажется довольно сложным вариантом использования.

Итак, на данный момент у меня есть эти 2 HoC, которые, я чувствую, мне нужно как-то реорганизовать, чтобы использовать хуки, называемые withSVG and withCanvas . Они настраивают другой DOM для представления, и, что важно, у одного есть вызываемая функция renderLoop , которую необходимо вызвать из компонента Scatter ниже.

Немного странно, потому Scatter что теперь это просто бизнес-логика (использование зависимостей в useEffect), на самом деле ему не нужно возвращать какой-либо DOM, потому что он просто манипулирует DOM родительского HoC. Однако я ожидаю еще много таких компонентов, как Scatter в будущем, поэтому не хочу переносить эту логику в HOC (если они вообще подходят для этого).

 const duration = 2000;

const withSVG = ({ width, height, ...props }) => WrappedComponent => {
    const layer = useRef(null);

    return (props) => (
        <g width={width} height={height} ref={layer}>
            <WrappedComponent {...props} layer={layer} />
        </g>
    );
};

const withCanvas = ({ width, height, ...props }) => WrappedComponent => {
    const layer = useRef(null);
    const canvas = useRef(null);
    const renderLoop = getRenderLoop(canvas.current, width, height);

    return (props) => (
        <React.Fragment>
            <custom ref={layer}/>
            <canvas width={width} height={height} ref={canvas}></canvas>
            <WrappedComponent {...props} layer={layer} renderLoop={renderLoop} />
        </React.Fragment>
    );
};

const Scatter = ({ data, layer, renderLoop }) => {
    useEffect(() => {
        if (!layer.current) { return; }

        // D3 data join
        const join = d3
          .select(layer.current)
          .selectAll("circle")
          .data(data, d => d.key);

        // Shrink the circles to 0 size
        const exit = join.exit()
            .transition("radius")
            .duration(duration)
            .attr("r", 0)
            .remove();

        const enter = join.enter()
            .append("circle")
            .attr("cx", d => d.x)
            .attr("cy", d => d.y)
            .attr("r", 0)
            .style("fill", d => d.color);

        const update = enter
            .merge(join)
            .transition("radius")
            .duration(duration)
            .attr("cx", d => d.x)
            .attr("cy", d => d.y)
            .attr("r", 30)
            .style("fill", d => d.color);
            
        if (renderLoop) {
            renderLoop(exit, update);
        }

    }, [layer, data]);

    return null;
};

const CanvasScatter = withCanvas(Scatter);
const SVGScatter = withSVG(Scatter);




// index.js
const width = 400;
const height = 400;

const App = () => {
    
    const [data, setData] = useState([
        {
            key: 1,
            x: 50,
            y: 50,
            color: "red"
        },
        {
            key: 2,
            x: 150,
            y: 150,
            color: "blue"
        },
    ]);

    setTimeout(() => {
        setData([
            {
            key: 1,
            x: 100,
            y: 100,
            color: "orange"
        },
        {
            key: 3,
            x: 150,
            y: 50,
            color: "green"
        },
        ]);
    }, 3000);

    return (
        <div>
            <svg width={width} height={height}>
                <SVGScatter width={width} height={height} data={data} />
            </svg>
            <CanvasScatter width={width} height={height} data={data} />
        </div>
    );

    // return <div>Hello React,Webpack 4 amp; Babel 7!</div>;
};

ReactDOM.render(<App />, document.querySelector("#root")); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="root"></div> 

Комментарии:

1. Я не уверен, что вы подразумеваете под «вы не можете вернуть функциональный компонент из HoC»… Компоненты более высокого порядка используют компонент react и возвращают компонент react. Является ли это классовым или функциональным компонентом, не имеет значения.

2. @DrewReese ах, может быть, я неправильно истолковал свою ошибку или неправильно выполнил свою HoC. Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render Я обновился, чтобы попытаться отразить состояние, в котором я нахожусь, немного больше — возможно, мой подход к чему-то приводит

Ответ №1:

Проблема

Я думаю, что вы неправильно сформировали свои компоненты более высокого порядка. Похоже, вы определили их для использования некоторых реквизитов, а затем компонента для переноса.

 const withSVG = ({ width, height, ...props }) => WrappedComponent => {...}
 

но вы вызываете их, как и ожидалось

 const SVGScatter = withSVG(Scatter);
 

Здесь Scatter сначала используется компонент, и HOC пытается извлечь из него значения и возвращает функцию для использования компонента, который является undefined . Я думаю, что это вызывает ошибку, которую вы видите.

Решение

В зависимости от того, как вы используете оформленные компоненты

 <SVGScatter width={width} height={height} data={data} />
<CanvasScatter width={width} height={height} data={data} />
 

Я думаю, вы имели в виду width деструкцию и height из внутреннего компонента, того, который возвращается HOC.

 const withSVG = WrappedComponent => ({ width, height, ...props }) => {
  const layer = useRef(null);

  return (
    <g width={width} height={height} ref={layer}>
      <WrappedComponent
        layer={layer}
        {...props} // <-- data passed here
      />
    </g>
  );
};

const withCanvas = WrappedComponent => ({ width, height, ...props }) => {
  const layer = useRef(null);
  const canvas = useRef(null);
  const renderLoop = getRenderLoop(canvas.current, width, height);

  return (
    <React.Fragment>
      <custom ref={layer}/>
      <canvas width={width} height={height} ref={canvas}></canvas>
      <WrappedComponent
        layer={layer}
        renderLoop={renderLoop}
        {...props} // <-- data passed here
      />
    </React.Fragment>
  );
};
 

Комментарии:

1. Спасибо, вы были абсолютно правы. Замена их и несколько других исправлений кода, и это стоит того. Очевидно, я немного запутался в сообщениях об ошибках и подумал, что я нахожусь на пути в никуда, чего не было: D