为什么钩子中的函数依赖项不会导致无限渲染

时间:2019-06-15 20:16:48

标签: javascript reactjs react-hooks

以下是代码段link to codesandbox

// function getFetchUrl(query) {
//   return "https://hn.algolia.com/api/v1/search?query=" + query;
// }
function App() {
  const [reactResult, setReactResult] = useState(null);
  const [reduxResult, setReduxResult] = useState(null);
  function SearchResults() {
    // ? Re-triggers all effects on every render
    // const getFetchUrl = useCallback((query) =>  {
      // return "https://hn.algolia.com/api/v1/search?query=" + query;
    // }, []);


     function getFetchUrl(query) {
       return "https://hn.algolia.com/api/v1/search?query=" + query;
     }

    useEffect(() => {
      console.log("running effect: 15");
      setReactResult(getFetchUrl("react"));
      // ... Fetch data and do something ...
      // }, [getFetchUrl]); // ? Deps are correct but they change too often
    }, [getFetchUrl]);

    useEffect(() => {
      console.log("running effect: 21");
      setReduxResult(getFetchUrl("redux"));
      // ... Fetch data and do something ...
      // }, [getFetchUrl]); // ? Deps are correct but they change too often
    }, [getFetchUrl]);

    // ...
  }

  SearchResults();

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>{reactResult}</h2>
      <h2>{reduxResult}</h2>
    </div>
  );
}

控制台中的输出为

running effect: 15
running effect: 21
running effect: 15
running effect: 21

我已经签出了this的答案,并且得到了重新定义函数的信息,这导致useEffect再次运行(第二次)。但我想澄清一个疑问:

useEffect第二次运行时,它调用stateSetter函数(要求React再次渲染组件)。

所以上面的代码段不应该无限循环运行吗?

示例和基本理解摘自A Complete Guide to useEffect

2 个答案:

答案 0 :(得分:3)

在使用useState时,如果状态值足够高,react非常聪明,可以跳过重新渲染 尽管调用了setState函数,实际上并没有改变。 (记录在https://reactjs.org/docs/hooks-reference.html#bailing-out-of-a-state-update中)

您所包含的示例代码与链接的代码笔略有不同,实际上只会导致控制台记录消息的单个“集合”。

running effect: 15
running effect: 21

事件的顺序是:

  1. 初始渲染,同时触发效果和更新reactResultreduxResult状态。这样就可以重新渲染。
  2. 该组件重新渲染。在您包含的示例中,您使用的是useCallback,没有依赖性,该依赖性将返回先前的值,因此效果将无法运行。

另一方面,在您的代码笔中,您无需在每次执行时重新定义回调,而无需useCallback,在这种情况下,您将获得两套“控制台”控制台消息:

  1. 初始渲染,同时触发效果和更新reactResultreduxResult状态。这样就可以重新渲染。
  2. 该组件重新渲染。 getFetchUrl是本地功能,因此上次运行的等于getFetchUrl。结果,效果将重新运行。但是setReactResultsetReduxResult的调用值都与以前相同,因此将触发重新渲染。

答案 1 :(得分:0)

仅当getFetchUrl发生更改时才会触发您的效果……并且由于它是一个已记录的回调(不会更改),因此效果仅运行一次。