在更高阶的组件中使用钩子

时间:2019-02-23 01:20:17

标签: reactjs

我想开发一个新功能,该功能以前会使用钩子包含在一个更高阶的组件中。但是,由于根据React Documentation

“您不能在类组件内部使用Hooks,但是您绝对可以在单个树中将类和函数组件与Hooks混合使用。”

因此,假设我有一些现有的类组件ExistingComponent,我想使用其他功能进行扩展,例如,监听window.resize。我希望这样做。

// Existing Component
export class ExistingComponent extends React.Component {
  render() {
    return <div>I exist!</div>;
  }
}

// Hook
export const useWindowResize = () => {
  function resize() {
    console.log('window resized!');
  }

  useEffect(() => {
    window.addEventListener('resize', resize);
    return function cleanup() {
      window.removeEventListener('resize', resize);
    };
  });
};

// HOC
export const withWindowResize = component => {
  useWindowResize();
  return component;
};

// Extended Component
export const BetterComponent = withWindowResize(ExistingComponent);

但是,这在Uncaught Invariant Violation: Hooks can only be called inside the body of a function component.上失败了,我确实使用了react-hot-loader,但是我仍然能够在不返回类组件的组件函数中使用钩子。另外,我可以从函数中删除useWindowResize()并按预期方式呈现。

我也能够呈现文档中提供的示例,因此我知道一般来说钩子不是问题:

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

这是错误的方法吗?

1 个答案:

答案 0 :(得分:3)

您可以从withWindowResize HOC返回一个新的函数组件,在其中调用该钩子并将props散布在传入的组件上。

您还可以将空数组作为第二个参数传递给useEffect,以使其仅在初始渲染后运行一次。

const { useEffect } = React;

class ExistingComponent extends React.Component {
  render() {
    return <div>I exist!</div>;
  }
}

const useWindowResize = () => {
  useEffect(() => {
    function resize() {
      console.log('window resized!');
    }
    window.addEventListener('resize', resize);
    return () => {
      window.removeEventListener('resize', resize);
    };
  }, []);
};

const withWindowResize = Component => {
  return (props) => {
    useWindowResize();
    return <Component {...props} />;
  }
};

const BetterComponent = withWindowResize(ExistingComponent);

ReactDOM.render(<BetterComponent />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>