简体   繁体   English

是否可以将此 HoC 模式转换为 React Hook 语法?

[英]Is it possible to turns this HoC pattern into a React Hook syntax?

I'm trying to create a reusable component, which alters it's behaviour - essentially rendering to either SVG or Canvas.我正在尝试创建一个可重用的组件,它会改变它的行为——本质上是渲染到 SVG 或 Canvas。 I'm currently trying to do this by wrapping it up in a HoC (however I'm trying to use React hooks) so this is all falling a bit flat on it's face.我目前正试图通过将它包装在一个 HoC 中来做到这一点(但是我正在尝试使用 React 钩子),所以这一切都显得有些平淡。


Edit with extra info使用额外信息进行编辑

With the current approach (HoC returning a function) I get the following error:使用当前方法(HoC 返回函数),我收到以下错误:

Functions are not valid as a React child.函数作为 React 子元素无效。 This may happen if you return a Component instead of from render.如果您返回 Component 而不是从 render 返回,则可能会发生这种情况。 Or maybe you meant to call this function rather than return it.或者,您可能打算调用此函数而不是返回它。

If I remove the function call within the HoC's:如果我删除了 HoC 中的函数调用:

React.jsx: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: object React.jsx:类型无效——需要一个字符串(对于内置组件)或一个类/函数(对于复合组件)但得到:对象


I've seen an example of converting HoC's to React hooks, but I'm struggling to work out how I might convert this - if it's even possible and wondering if anyone can give me a pointer?我已经看到了一个将 HoC 转换为 React 钩子的例子,但我正在努力弄清楚如何转换它 - 如果它甚至可能并且想知道是否有人可以给我一个指针? I might have tried to architect this the wrong way, as it feels like quite a complex use case.我可能试图以错误的方式构建它,因为它感觉像是一个相当复杂的用例。

So I've these 2 HoC's at the moment which I feel like I need to refactor somehow to use hooks called withSVG and withCanvas .所以我现在有这两个 HoC,我觉得我需要以某种方式重构以使用称为withSVGwithCanvas钩子。 They setup different DOM to be represented and importantly one has a function called renderLoop which needs to be called from the Scatter component below.他们设置了不同的 DOM 来表示,重要的是有一个叫做renderLoop的函数,它需要从下面的 Scatter 组件中调用。

Slightly quirky, is because the Scatter is just business logic now (leveraging dependencies in the useEffect), it doesn't actually need to return any DOM, because it's just manipulating the DOM of the parent HoC.有点古怪,因为Scatter现在只是业务逻辑(利用 useEffect 中的依赖项),它实际上不需要返回任何 DOM,因为它只是操作父 HoC 的 DOM。 I'm expecting many more components like Scatter in the future however so don't want to move this logic into the HoCs (if they're even the right way of doing this).我期待在未来有更多像Scatter这样的组件,所以不想把这个逻辑移到 HoCs 中(如果他们甚至是这样做的正确方法)。

 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 => dx) .attr("cy", d => dy) .attr("r", 0) .style("fill", d => d.color); const update = enter .merge(join) .transition("radius") .duration(duration) .attr("cx", d => dx) .attr("cy", d => dy) .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 & 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>

Issue问题

I think you have malformed your Higher Order Components.我认为您的高阶组件格式不正确。 It appears you've defined them to consume some props, then a component to wrap.看起来你已经定义了它们来消耗一些道具,然后是一个要包装的组件。

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

but you invoke them as expected但是您按预期调用它们

const SVGScatter = withSVG(Scatter);

Here the Scatter component is consumed first and the HOC tries to destructure values from it and returns a function to consume a component, which is undefined .这里首先使用Scatter组件,HOC 尝试从中解构值并返回一个函数来使用一个undefined的组件。 I think this causes the error you are seeing.我认为这会导致您看到的错误。

Solution解决方案

Based on how you use the decorated components基于你如何使用装饰组件

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

I think you meant to destructure width and height from the inner component, the one that is returned by the HOC.我认为您的意思是从内部组件(由 HOC 返回的组件)解构widthheight

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>
  );
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM