useEffect挂钩-依赖项-重新渲染问题

时间:2020-09-03 20:15:58

标签: reactjs react-hooks

第一种情况:
_假设我有一个处于redux状态或父状态的道具。
_我不希望在此道具发生更改时触发useEffect,
_但是我确实需要在useEffect中使用道具。
_React警告我将prop添加到依赖项数组,但是如果这样做,则useEffect将再次触发。

第二种情况:
_我正在useEffect中使用函数,
_但是该功能在其他地方也需要。
_不想重复功能代码。
_React希望我将函数添加到依赖项数组中,但是我不希望每次函数引用发生更改时都触发useEffect。

4 个答案:

答案 0 :(得分:2)

在使用useEffect时,您应该考虑闭包。如果在useEffect中使用了任何props或局部变量,则建议将其包含在依赖项数组中,否则由于关闭,您将使用过时的数据。

在这里,我介绍一个用例。在ComponentUsingRef内部,我们使用ref作为容器。您可以在https://reactjs.org/docs/hooks-reference.html#useref

上找到更多有关它的信息。

这种方法的优点是您不必在其中记住fn 您的父组件。您将始终使用的最新值 函数,在给定的情况下,它甚至不会引起useEffect的触发,因为它不会取决于您的依赖项


const Component=({fn})=>{
    useEffect(()=>{
       fn()
    },[fn])
    .....
    .....
    return <SomeComponent {...newProps}/>
}

const ComponentUsingRef=({fn}){
    const fnRef = useRef(fn)
    fnRef.current = fn // so that our ref container contains the latest value
    useEffect(()=>{
       fn.current()
    },[ ])
    .....
    .....
    return <SomeComponent {...newProps}/>
}

如果要使用抽象,则应在custom hook

中提取逻辑

答案 1 :(得分:1)

如果唯一的问题是警告,那么请不要担心,如果效果是唯一使用的道具,则可以简单地禁用效果规则,并从依赖项数组中忽略该道具。对于具有多个依赖项的效果,可以使用useRef来存储道具的最新值而不触发效果,如下所示:

const Comp = ({someProp, ...props}) => {
  const myProp = useRef(someProp);
  myProp.current = someProp;

  // this effect won't fire when someProp is changed
  React.useEffect(() => {
    doSomething(myProp.current, props.a, props.b);
  }, [props.a, props.b, myProp]);
}

对于第二种情况,我可能会将该函数放在单独的文件中,并将其导入将要使用的组件中,但是,对于第二种情况,您可以使用相同的模式。

答案 2 :(得分:0)

要忽略反应给您的警告,可以通过在// eslint-disable-next-line react-hooks/exhaustive-deps代码中添加useEffect来禁用警告。您可以在React文档的Rules of Hooks部分中阅读有关此内容的更多信息。这些规则包含在eslint-plugin-react-hooks package中。

要防止在每次函数引用更改时都触发useEffect,可以使用useCallbackuseCallback钩子将存储对函数的引用,而不是函数本身。仅当更新函数依赖项之一时,函数参考才会更新。如果您不希望函数引用永远进行更新,则可以使用与useEffect挂钩的依赖项数组相同的方式将依赖项数组留空。

Here is a working example of both

import React, { useState, useEffect, useCallback } from "react";
import "./styles.css";

export default function App() {
  const [value, setValue] = useState(0);

  const wrappedFunction = useCallback(
    (caller) => {
      const newValue = value + 1;
      setValue(newValue);
      console.log(`Firing function from ${caller}: ${newValue}`);
    },
    [value]
  );

  useEffect(() => {
    console.log(`Logging from useEffect: ${value}`);
    wrappedFunction("useEffect");
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <div>{value}</div>
      <button onClick={() => wrappedFunction("button click")}>
        Fire function
      </button>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

答案 3 :(得分:0)

请参阅此链接以获取第一个问题Sandbox

在这里,我们仅使用状态变量来保存初始值,无论它是来自Parent还是Store,所以即使在Parent或store中更改值,子级仍将使用旧值,直到除非它使用useEffect更新它。

父母

export default function App() {
  const [state, setState] = useState("monika");
  return (
    <div className="App">
      <h1>My Name is {state}</h1>
      <button
        onClick={() => {
          setState("Rohan");
        }}
      >
        change name
      </button>
      <First username={state} />
    </div>
  );
}

孩子

export default function First({ username }) {
  const [name, setName] = useState(username);
  return (
    <div>
      <h1>My Name is {name}</h1>
    </div>
  );
}