何时使用useCallback,useMemo和useEffect

时间:2019-07-05 23:10:57

标签: reactjs react-hooks

useCallback,useMemo和useEffect之间的主要区别是什么。 ?。给出何时使用useCallback,useMemo和useEffect的示例。

5 个答案:

答案 0 :(得分:9)

useEffect()将使您可以根据发送给它的依赖项在组件上创建副作用。

function Example() {
  const [count, setCount] = React.useState(0);

  React.useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, [count]);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

ReactDOM.render(<Example />, document.getElementById('root'))
<script src="https://unpkg.com/react@16.8.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.8.0/umd/react-dom.development.js"></script>

<div id="root"></div>

上面的示例是taken from the documentation of React。您可以看到,每次单击按钮都会触发count字段的更新(使用setCount()),然后取决于count变量的效果将触发页面标题的更新。


useCallback()将返回memoized回调。通常,如果您有一个子组件接收一个函数prop,则在每次重新渲染父组件时,都会重新执行此函数;通过使用useCallback(),可以确保仅在依赖项数组上的任何值更改时才重新执行此函数。

function ExampleChild({ callbackFunction }) {
  const [value, setValue] = React.useState(0);

  React.useEffect(() => {
    setValue(value + 1)
  }, [callbackFunction]);

  return (<p>Child: {value}</p>);
}

function ExampleParent() {
  const [count, setCount] = React.useState(0);
  const [another, setAnother] = React.useState(0);
  
  const countCallback = React.useCallback(() => {
    return count;
  }, [count]);
  
  return (
    <div>
      <ExampleChild callbackFunction={countCallback} />
      <button onClick={() => setCount(count + 1)}>
        Change callback
      </button>
      
      <button onClick={() => setAnother(another + 1)}>
        Do not change callback
      </button>
    </div>
  )
}

ReactDOM.render(<ExampleParent />, document.getElementById('root'));
<script src="https://unpkg.com/react@16.8.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.8.0/umd/react-dom.development.js"></script>

<div id="root"></div>


useMemo()将返回一个memoized值,该值是传递的参数的结果。这意味着useMemo()将对某个参数进行一次计算,然后将从缓存中为同一参数返回相同的结果。

当您需要处理大量数据时,这非常有用。

function ExampleChild({ value }) {
   const [childValue, setChildValue] = React.useState(0);

   React.useEffect(() => {
     setChildValue(childValue + 1);
   }, [value])

   return <p>Child value: {childValue}</p>;
}

function ExampleParent() {
  const [value, setValue] = React.useState(0);
  const heavyProcessing = () => {
    // Do some heavy processing with the parameter
    console.log(`Cached memo: ${value}`);
    return value;
  };

  const memoizedResult = React.useMemo(heavyProcessing, [value]);
  
  return (
    <div>
      <ExampleChild value={memoizedResult} />
      <button onClick={() => setValue(value + 1)}>
        Change memo
      </button>
    </div>
  )
}

ReactDOM.render(<ExampleParent />, document.getElementById('root'));
<script src="https://unpkg.com/react@16.8.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.8.0/umd/react-dom.development.js"></script>

<div id="root"></div>

答案 1 :(得分:4)

简短说明。

useEffect

它是类组件生命周期方法(componentDidMountcomponentWillUnmountcomponentDidUpdate,...)的替代方法,您还可以基于某些依赖项将其用作副作用, “当某些变量更改时,执行此操作。”

useCallback

在每个渲染中,再次运行组件(功能)内部的所有内容,因此,如果某个子组件依赖于父组件中的某个功能,则子组件将在每次父渲染时重新渲染,即使该函数“不更改”(引用会更改,但是功能将不更改)。
它用于优化,避免了不必要的子级渲染,仅在某些依赖项更改时使函数更改引用。 当函数是副作用的依赖项时,例如应该使用它useEffect

useMemo

它将在每个渲染器上运行,但具有缓存的值。仅在某些依赖项更改时才使用新值。当您有昂贵的计算时,它用于优化。 Here is also a good answer that explains it

答案 2 :(得分:2)

最少的解释:

useEffect:

只要您有对状态变化作出反应或在即将发生变化之前执行某些逻辑。

useEffect(() => {
  // execute when state changed
  () => {
    // execute before state is changed
  }
}, [state]);

或在没有依赖性的情况下:

useEffect(() => {
  // execute when component has mounted
  () => {
    // execute when component will unmount
  }
}, []);

useCallback:

只要您具有取决于某些状态的功能。此挂钩用于性能优化,并防止重新分配组件内部的功能,除非更改依赖状态。

const myFunction = useCallback(() => {
  // execute your logic for myFunction
}, [state]);

没有useCallback的话,myFunction将在每个渲染器上重新分配。因此,与useCallback相比,它将使用更多的计算时间。

useMemo

只要您有一个值取决于特定状态。与useCallback一样,useMemo可以减少重新分配以优化性能。

const myValue = useMemo(() => {
  // return calculated value
}, [state]); 

与useCallback一样,仅在状态更改时才分配myValue,因此将减少计算时间。否则,将在每个渲染器上重新分配myValue。

!模仿mimick componentWillMount生命周期

useMemo(() => {
  // execute componentWillMount logic
]}, []);

因为useEffect在第一个渲染之后被调用,然后在每次依赖更改时被调用。它永远不会在第一个渲染之前运行。 useMemo与您的JS内联执行,因此将在到达您的Components返回语句之前执行。

!注意:具有useCallback的函数和具有useMemo的值可以用作useCallback,useMemo和useEffect中的依赖项。强烈建议使用这些挂钩,以使组件中的结构状态清晰易读。这些挂钩不会触发渲染。只有useState和useReducer可以!

如果要保持不触发重新渲染的状态或上述任何挂钩,则可以使用useRef。 useRef 将在渲染过程中保持一致的值,而不会触发任何状态相关的值或效果。

答案 3 :(得分:1)

useEffect

在组件安装,卸载以及任何依赖项更改时调用。

可用于在组件为mountedsubscribeunsubscribe时获取数据,以在组件mountsunmounts时获取事件流(请考虑rxjs)。

const [userId, updateUser] = useState(1);

useEffect(()=>{
  //subscription
   const sub = getUser(userId).subscribe(user => user);

// cleanup
  return () => {
   sub.unsubscribe();
 }

},[userId]) // <-- Will get called again when userId changes

也可以用于不需要清理的一次性方法

useEffect(()=>{

  oneTimeData();

},[]); // pass empty array to prevent being called multiple times


useCallback

  1. 您不想在每个组件渲染器上重新创建功能吗?

  2. 是否需要在组件安装或卸载时未调用的功能?

使用useCallback

const [val, updateValue] = useState(0);

const Compo = () => {

/* inc and dec will be re-created on every component render. 
   Not desirable a function does very intensive work.
*/

const inc = () => updateValue(x => x + 1);
const dec = () => updateValue(x => x - 1);

return render() {
   <Comp1 onClick={inc} />
   <Comp2 onClick={dec} />
 }
}

useCallback进行救援

const [val, updateValue] = useState(0);

const Compo = () => {

const callbackInc = useCallback(() => {
  setCount(currentVal => currentVal + 1);
}, []);

const callbackDec = useCallback(() => {
  setCount(currentVal => currentVal - 1);
}, []);

return render() {
   <Comp1 onClick={callbackInc} />
   <Comp2 onClick={callbackDec} />
 }
}

如果传递给setCount的参数不是函数,则必须在依赖项数组中指定要让useCallback“注意”的变量,否则不会产生更改效果

const callbackInc = useCallback(() => {
  setCount(val + 1); // val is an 'outside' variable therefore must be specified as a dependency
}, [val]);

useMemo

正在执行大量处理,并想memoize缓存)结果吗?使用useMemo

/*
  heavyProcessFunc will only be called again when either val or val2 changes
*/
const result = useMemo(heavyProcessFunc(val, val2),[val,val2])

答案 4 :(得分:1)

知道什么时候使用这些函数很好,但我想知道它们之间的实际区别是什么!这是我发现的:

  • useMemo 立即运行代码,因此返回值可用于后面的代码。这意味着它在第一次渲染之前运行,因此您用来访问 HTML 组件的任何 useRef 在初始运行时都将不可用。 (但您可以将 ref.current 添加到 useMemo 依赖项,以便在第一次渲染后再次运行 useMemo 代码,当 useRef 值可用时)。由于返回值可用于直接跟随它的代码,这就是为什么建议用于不需要在每次渲染时重新运行的复杂计算的原因,因为返回值立即可用可以使您不必弄乱状态现在存储值并稍后访问它 - 只需获取 useMemo() 的返回值并立即使用它。
  • useEffect 不会立即运行,而是在第一次渲染之后运行。这意味着任何引用 HTML 元素的 useRef 值在第一次运行时都是有效的。由于它在函数中的所有代码都完成并呈现后运行,因此没有返回值的意义,因为没有进一步运行的代码可以使用它。 useEffect 代码可以执行任何可见操作的唯一方法是更改​​状态以导致重新渲染,或直接修改 DOM。
  • useCallbackuseMemo 相同,除了它记住函数本身而不是它的返回值。这意味着 useCallback 函数不会立即运行,但可以稍后运行(或根本不运行),而 useMemo 会立即运行其函数并保存其返回值以供以后使用。与 useMemo 不同,这不适用于复杂的计算,因为每次使用时代码都会再次运行。

例如,如果我们将相同的函数传递给 useMemouseCallback

let input = 123;
const output = useMemo(() => {
  return input + 1;
}, [
  input,
]);

// The above function has now run and its return value is available.

console.log( output ); // 124

input = 125; // no effect as the function has already run
console.log( output ); // 124
let input = 123;
const output = useCallback(() => {
  return input + 1;
}, [
  input,
]);

// The above function has not run yet but we can run it now.

console.log( output() ); // 124

input = 125; // changes the result as the function is running again
console.log( output() ); // 126

这里,useCallback 已经记住了该函数,并且会在以后的渲染中继续返回原始函数,直到依赖项发生变化,而 useMemo 实际上立即运行该函数并且只记住它的返回值。

useCallback()useMemo() 都提供可以立即使用的返回值,而 useEffect() 则不提供,因为它的代码直到渲染完成后很晚才运行。