React hooks 中的依赖数组真的需要详尽吗?

Nis*_*ano 6 reactjs react-hooks

根据React 文档

效果函数内引用的每个值也应该出现在依赖项数组中

如果我的效果函数引用了外部作用域中的一些变量,但我只想在其中一个变量发生更改时执行它,为什么我需要指定依赖项数组中的所有其他变量?是的,如果其他变量发生变化,闭包就会变得过时,但我不在乎,因为我还不需要调用该函数。当我关心的变量发生变化时,可以调用具有当时值的新闭包。我缺少什么?

这是一个工作示例(据我所知),其中 useEffect 依赖项数组并不详尽:

import React, { useEffect, useState } from "react";

const allCars = {
    toyota: ["camry", "corolla", "mirai"],
    ford: ["mustang", "cortina", "model T"],
    nissan: ["murano", "micra", "maxima"],
};

function CarList() {
    const [cars, setCars] = useState([]);
    const [brand, setBrand] = useState("toyota");
    const [filterKey, setFilterKey] = useState("");

    useEffect(() => {
        // I don't want to run this effect when filterKey changes because I wanna wrap that case in a timeout to throttle it.
        setCars(allCars[brand].filter(model => model.startsWith(filterKey)));
    }, [brand]);

    useEffect(() => {
        // This effect is only called when filterKey changes but still picks up the current value of 'brand' at the time the function is called.
        const timeoutId = setTimeout(() => {
            setCars(allCars[brand].filter(model => model.startsWith(filterKey)));
        }, 500);
        return () => clearTimeout(timeoutId);
    }, [filterKey]);

    const handleChangeFilterKey = event => {
        setFilterKey(event.target.value);
    };

    return (
        <div>
            {`${brand} cars`}
            <div>Select brand</div>
            <input type="radio" value="toyota" checked={brand === "toyota"} onChange={() => setBrand("toyota")} />
            <input type="radio" value="ford" checked={brand === "ford"} onChange={() => setBrand("ford")} />
            <input type="radio" value="nissan" checked={brand === "nissan"} onChange={() => setBrand("nissan")} />
            <div>Filter</div>
            <input label="search" value={filterKey} onChange={handleChangeFilterKey} />
            <ul>
                {cars.map(car => (
                    <li>{car}</li>
                ))}
            </ul>
        </div>
    );
}
Run Code Online (Sandbox Code Playgroud)

上面的例子有没有什么陷阱?

nol*_*lan 2

是的,你应该一直遵循这个规则,当你发现你的代码因遵循它而中断时,这意味着没有遵循良好的实践。这就是这条规则的意义,确保你设计得很好。


我想在你的情况下,代码如下所示:

  const Test = () => {
   const [wantToSync] = useState(0)
   const [notWantToSync] = useState(0) // notWantToSync also might come from props, i'll use state as example here
   useEffect(() => {
    fetch('google.com', {
      body: JSON.stringify({wantToSync, notWantToSync})
    }).then(result => {
      // ...
    })
   }, [wantToSync]) // component is supposed to be reactive to notWantToSync, missing notWantToSync in dep is dangerous
 }
Run Code Online (Sandbox Code Playgroud)

如果notWantToSync被定义为组件的状态,则该组件应该对其做出反应,包括useEffect。如果这不是您想要的,notWantToSync则不应从一开始就声明。

const Test = () => {
  const [wantToSync] = useState(0)
  const notWantToSyncRef = useRef(0) // hey I don't want notWantToSync to be reactive, so i put it in useRef
  useEffect(() => {
    fetch('google.com', {
      body: JSON.stringify({wantToSync, notWantToSync: notWantToSyncRef.current})
    }).then(result => {
      // ...
    })
  }, [wantToSync, notWantToSyncRef]) // yeah, now eslint doesn't bother me, and notWantToSync doesn't trigger useEffect anymore
}
Run Code Online (Sandbox Code Playgroud)

通常,您不需要在 useEffect 中执行 if else 来停止重新渲染,除了它们具有不同的使用上下文之外,还有其他类似useMemouseCallback类似的方法useRef


我在新示例中看到您的挣扎,因此您想做一个节流阀,如果将filterKey其添加到第一个 useEffect dep,则节流阀将被破坏。我的观点是,当您发现自己处于这种情况时,通常意味着有更好的实践(eslint 详尽的帮助识别它),例如,将节流逻辑提取到挂钩: https: //github.com/streamich/react -use/blob/master/src/useThrottle.ts

详尽的 deps 并不是重要的事情,它只是确保代码设计良好的良好实践。上面的例子证明了

  • 感谢您的回复。这并不能比文档中的引用更能回答问题。当您说它将确保我不会错过必要的依赖项时,您就是在假设我正在质疑。鉴于我所说的内容,为什么依赖项是必要的。对我来说,这确实很痛。在一种情况下,我希望仅当一个变量更改时才调用我的函数,现在当任何变量更改时都会调用它,因此我必须添加额外的检查并使逻辑复杂化。此外,钩子现在需要扫描更长的数组,这在大多数情况下可能不是什么大问题,但为什么要添加 [cont...] (2认同)