浅谈 React Hook 10 条经验

1,571 阅读2分钟

1. 合理使用 useMemo 取代 useState + useEffect

不要把父组件传递进来的 props 直接赋值给子组件的 state ,然后又监听父组件的 props

// bad
function Son ({data}) {
  const [fliterData, setFliterrData] = useState(data)
   useEffect(() => {
      setFliterrData(data => {...})
   }, [data])
}
// good
const filterData = useMemo(() => {
  return data.filter((item) => doSthing())
  );
}, [data]);

2. 使用 useMemo 和 useCallback 优化性能

// bad 
function Parent() {
  const [count, setCount] = useState(0);
  const handleIncrement = () => setCount(count + 1);
  return <Son onClick={handleIncrement}>+</Son>
}


// good 
function Parent() {
  const [count, setCount] = useState(0);
  const handleIncrement = useCallback(() => {
    setCount(count + 1);
  }, [count]);
  return <Son onClick={handleIncrement}>+</Son>;
}

如果属性不需要传递给自定义子组件,可以不使用缓存

3. useEffect 别依赖一个对象变量,且变量有默认值,例如 {}、[]

// bad 
function Parent({ data = {} }) {
  // const data = {} 每次都会产生新对象
  const handleIncrement = useCallback(() => {
    dispatch(fetch('xxxxx')); // 这段代码可能会被一直执行,因为 data 的引用地址一直在变
  }, [dispatch, data]);
  return <Son onClick={handleIncrement}>+</Son>;
}

// good 
function Parent({ data }) {
  const handleIncrement = useCallback(() => {
    dispatch(fetch('xxxxx')); 
  }, [dispatch, data]);
  return <Son onClick={handleIncrement}>+</Son>;
}

4. useEffect 依赖过多

// bad
function Component({id, name}) {
  useEffect(() => {
   /* do something */
    setId(id)
    setName(name)
  }, [id, name,...更多]) // 当依赖越多的时候,复杂度就越来越高
}

// good
function Component({id, name}) {

  useEffect(() => {
   /* do something */
    setId(id)
  }, [id])
  useEffect(() => {
    /* do something */
     setName(name)
   }, [name])
}

5. 万恶的 useCallback 的隐式嵌套

具体问题和解决办法

6. 带有业务逻辑的函数组件显示控制

带有业务逻辑的函数组件显示控制,例如弹框,他的显示隐藏最好由外部组件去控制,而不是在弹框组件内部去控制显示

// bad
function CustomModel({ visible }) {
  if (!visible) return null;
  return <Modal visible={visible}>我是弹框</Modal>;
}
// bad 
function CustomModel({ visible }) {
  useEffect(() => {
    if(visible) {
      // do something
    }
  }, [visible])
  return <Modal visible={visible}>我是弹框</Modal>;
}
fun
// good 
function Page() {
  visible && <CustomModel visible />
}

7. hooks 耦合度太高

当函数式组件的功能越强大,它的复杂度会越来越高,尤其是函数式组件里面使用了 hooks,经常看到一个 Function Component,五百行甚至上千行的代码。 不要忘记拆分复杂度,就跟函数一样,调用其他函数,而不是把所有逻辑放到一个函数里。

如何让代码更优雅,有两条建议

  1. 函数逻辑的复用
  2. 函数逻辑的拆分

8. hooks 不是万能的

我们写 hooks 只是复用 js 逻辑,并不能复用 UI,当复用 UI 的时候,高阶组件和 render props 还是必须要用的

9.state 优先使用事件驱动,而不是依赖

  1. 事件驱动 state 变化,页面更新
  2. effect 里面去驱动 state,页面更新

最好先使用事件,再用 effect

10. 父组件调用子组件方法

forwardRef 结合 useImperativeHandle

//bad

const Son = forwardRef((props, ref) => {
  const open = () => {
    console.log('open');
  };
  const otherMethod = () => {
    console.log('otherMethod');
  };
});

function Parent() {
  const ref = useRef();
  useEffect(() => {
    ref.current.open();
  }, []);

  return (
    <>
      <div>demo</div>
      <Son ref={ref}></Son>
    </>
  );
}

// good 
const Son = forwardRef((props, ref) => {
  useImperativeHandle(ref, () => ({
    open() {
      console.log('open');
    },
  }));
  const otherMethod = () => {
    console.log('otherMethod');
  };
});

function Parent() {
  const ref = useRef();
  useEffect(() => {
    ref.current.open();
  }, []);

  return (
    <>
      <div>demo</div>
      <Son ref={ref}></Son>
    </>
  );
}