将函数传递给UseEffect是否有效?可以这样做来代替useCallback吗?

时间:2020-04-21 16:02:08

标签: reactjs react-hooks

让我说一下

// Get a hook function
const {
  useState,
  useEffect
} = React;

const Example = ({
  title
}) => {
  const [formVal, setFormVal] = useState("")
  const [count, setCount] = useState(0);

  const increment = () => setCount(count + 1)

  // now use increment as just the cb for useeffect
  useEffect(increment, [formVal])

  return ( <
    div >
    <input onChange={(e) => setFormVal(e)} placeholder="test"/>
    <
    p > {
      title
    } < /p> <
    p > You interacted {
      count
    }
    times < /p> <
    button onClick = {
      increment
    } >
    Click me <
    /button> < /
    div >
  );
};

// Render it
ReactDOM.render( <
  Example title = "Example using Hooks:" / > ,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>

我没有在increment匿名回调中调用useEffect,而是将整个内容作为第一个参数。我的问题是,这是否消除了将increment包装到useCallback中的需要?

请注意,这是一个超级设计的示例,在此假设同时发生了其他重新渲染,并且目标是不要有无用的提交或重新渲染。

2 个答案:

答案 0 :(得分:1)

useEffect和useCallback之间有区别。

当依赖项数组更改时,

useEffect调用函数回调。当依赖项数组更改时,useCallback会使用newclosure重新创建该函数。同样useCallback返回一个函数useEffect没有

因此,即使您可以将增量传递给useEffect来实际执行该函数,它也不等同于useCallback,其中在依赖项数组发生更改时才重新创建该函数。在您的情况下,每个渲染上按钮元素的增量功能引用将有所不同

实现上述代码的最佳方法是

// Get a hook function
const {
  useState,
  useEffect,
  useCallback
} = React;

const Example = ({
  title
}) => {
  const [formVal, setFormVal] = useState("")
  const [count, setCount] = useState(0);

  const increment = useCallback(() => setCount(prevCount => prevCount + 1), [])

  
  useEffect(increment, [formVal])

  return ( <
    div >
    <input onChange={(e) => setFormVal(e)} placeholder="test"/>
    <
    p > {
      title
    } < /p> <
    p > You interacted {
      count
    }
    times < /p> <
    button onClick = {
      increment
    } >
    Click me <
    /button> < /
    div >
  );
};

// Render it
ReactDOM.render( <
  Example title = "Example using Hooks:" / > ,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>

即使您以增量或正常回调传递给useEffect,也仅在依赖项数组更改时更改useEffect用法的函数引用

答案 1 :(得分:1)

将函数传递给UseEffect是否有效?

是的。主要目的是保持功能的可重用性。确保它不返回任何内容/ / void或无参数函数() => {...}作为清除。否则,React会发出控制台错误。

这可以代替useCallback吗?

不。在useEffect之后,挂钩的行为完全相同:

const increment = () => setCount(count + 1) //  declared somewhere inside component

useEffect(increment, [formVal]) 
useEffect(() => setCount(count + 1), [formVal]) // inline variant of increment

在两个版本的每个渲染周期中都会创建一个新函数。 useEffect在渲染后立即调用此传入的函数,只要依赖项数组的一项formVal)发生变化- {{1 }}。


increment中缺少依赖项

有一个细微的错误:目前,useEffect引用了效果中的陈旧值count也需要useEffect作为依赖项,因为count使用了它。您可以内联increment效果回调,ESLint会警告您:increment。插图:

React Hook useEffect has a missing dependency: 'count'
const Example = () => {
  const [count, setCount] = React.useState(0);

  const increment = () => {
    // count dependency is missing / state closure scope
    // => state will always be reset to initial count value
    // we could use funtional updater, but this is just for demonstration
    const handle = setInterval(() => setCount(count + 1), 2000);
    return () => clearInterval(handle);
  };

  React.useEffect(increment, []);

  // this triggers ESLint react hooks warning (ESLint not supported on SO)
  // useEffect(() => { count; }, []);

  return (
    <div>
      <p>You interacted {count} times</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <p>Click the button. The interval resets counter every 2 sec. because of a stale count value</p>
    </div>
  );
};

ReactDOM.render(<Example />, document.getElementById("root"));


总体解决方案

在您的情况下,无需将<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script> <div id="root"></div>useCallback一起使用。请执行以下任一操作:

increment

为完善起见,// Add count effect dependency const increment = () => setCount(count + 1) useEffect(increment, [count, formVal]) // or use functional updater of `useState` const increment = () => setCount(prev => prev + 1) useEffect(increment, [formVal]) 解决方案:

useCallback

const increment = useCallback(() => setCount(count + 1), [count]) useEffect(increment, [increment, formVal]) 调用由其依赖项数组确定。以及useEffect和原始DOM节点are cheap in terms of performance的内联点击回调。因此,您不需要button

如果useCallback实现非常简单,您甚至可以在所有情况下内联它。