使用React hooks如何只让下面这段代码的子组件只render一次?

代码大致如下,子组件有自己的状态但受父组件的控制,当父组件count更新的时候,需要将子组件的number和父组件的count保持同步,但是使用use…
关注者
351
被浏览
131,880

41 个回答

作为给 ant design 贡献过代码的人,可以告诉你 antd 内部是用的 forwardRef 和 useImperativeHandle 来实现这个效果。这也是目前仅通过内部 hooks 来实现的最正确的做法。

看了一圈这个问题下的回答,只有 @maxcalibur 回答到这两个 function,但是却一个赞都没(黑人问号)。其他人要么直接说没法实现,要么通过各种绕弯子来实现,。。

另外拓展一下,用我这个基于观察者模式共享 state 的库来做也是可以实现的:

去看一下我写的 readme 下的 live example 就知道了,目前已在几家大大小小的公司中投产了。

还有一些其他不算方法的方法:

  1. 子组件直接用父组件传的 props 而不是用内部自己维护的 state 来渲染,然后通过 callback 反馈给父组件来刷新传进来的 props 也是可以的,但是子组件一定要自己维护 state 的情况就不能这么用了。
  2. 再另外,父组件渲染时改变一下子组件的 key 来强制重新创建 dom 也是可以实现题主要的效果的,只不过性能上会更差(狗头)。

那些说没法实现的不是被打脸么。

========= 08/17 更新 =========

感谢 @Dcab @Liuyl 在评论区的讨论。 @maxcalibur 的答案里之所以组件 B 没包 memo 也能最终只渲染一次,是因为 react 框架的 auto batchupdate 将两次 setState 优化为只 update 一次父组件了。

另外补充一下,forwardRef 和 useImperativeHandle 是用来将子组件的内部状态或方法挂载到 ref 上(通常在父组件创建)。

而要实现父组件刷新时子组件不跟随刷新的话,目前比较好的选择有:

  1. 用 memo 方法包裹子组件,然后确保传给子组件的 props 不变。
  2. 将子组件的创建(createElement)放到父组件的 render function 外,这也是 constate 这个库的基本原理之一,举个例子:
// 仅为大概,其余细节请自行脑补补齐
const A = React.forwardRef((_, ref) => {
  useImperativeHandle(ref, ...);
const B = React.forwardRef(({ children }, ref) => {
  const onClick = () => { ref.update() }
  return <>
    {children}
const App = () => {
  const ref = useRef(null);
  return <B ref={ref}>
    <A ref={ref} />

是这个意思吗 感觉其他答案都有点绕

const B = React.forwardRef((props: any, ref) => {
  const [state, setstate] = useState(0);
  useImperativeHandle(
    () => ({
      update: (n: any) => {
        setstate(n);
  console.log("child", state, props.count);
  return <div>{state}</div>;
const App = function () {
  const [state, setstate] = useState(0);
  const ref = useRef(null);
  return (
    <div className="App">
      <button
        onClick={() => {
          setstate(state + 1);
          (ref.current as any).update(state + 1);
        click
      </button>