React Hooks在依赖列表中省略函数是否安全?

728 阅读2分钟

一般来说,是不安全的。

const [value, setValue] = useState("");
function test() {
	console.log(value)
}
useEffect(() => {
  test()
},[])

上述的代码,是有bug的。因为外部函数使用了state,而如果在useEffect中使用了省略函数,effect中很难知道外部函数使用了哪些stateporps。所以我们应该在effect内部去声明所需要的函数,这样就能更好看的看出effect依赖组件作用域的那些值。

const [value, setValue] = useState("");
useEffect(() => {
  function test() {
		console.log(value)
	}
  test()
},[value])

如果你指定了一个依赖列表作为useEffectuseLayoutEffectuseMemouseCallBackuseImperativeHandle的最后一个参数,那么它必须包含回调中的所有值,并参与React数据流。这就包括propsstate,以及任何由它衍生而来的东西。

只有当函数以及它所调用的函数不引用propsstate以及由它们衍生而来的值时,才可以把它从依赖列表中省略。

常见数据请求问题:

function Demo ({ id }) {
  const [value, setValue] = useState("");
  asycn function test(){
    const res = await fetch('http://XXX' + id);
    const data = res.json();
    setValue(data)
  }
  useEffect(() => {
    test()
  },[])
}

这里的外部函数使用了state,所以我们需要把外部函数拿到effect中,这样可以更好的看出使用了哪些stateprops,并确保已经被声明了。

function Demo ({ id }) {
	const [value, setValue] = useState("");

  useEffect(() => {
    asycn function test(){
    const res = await fetch('http://XXX' + id);
    const data = res.json();
    setValue(data)
  }
    test()
  },[value])
}

如果因为某些原因无法把一个函数引入到effect内部,其他解决方法:

  1. 可以尝试把函数移动到组件外,这样这个函数就不会依赖任何propsstate,并且也不用出现在依赖列表中。
  2. 如果调用的方法是一个纯计算,并且可以在渲染时调用,你可以在effect之外调用它 并让 effect 依赖于它的返回值。
  1. 也可以把函数加入 effect 的依赖但把它的定义包裹进useCallback Hook。这就确保了它不随渲染而改变,除非它自身的依赖发生了改变:
function ProductPage({ productId }) {
  // ✅ 用 useCallback 包裹以避免随渲染发生改变
  const fetchProduct = useCallback(() => {
    // ... Does something with productId ...
  }, [productId]); // ✅ useCallback 的所有依赖都被指定了

  return <ProductDetails fetchProduct={fetchProduct} />;
}

function ProductDetails({ fetchProduct }) {
  useEffect(() => {
    fetchProduct();
  }, [fetchProduct]); // ✅ useEffect 的所有依赖都被指定了
  // ...
}