简体   繁体   English

何时使用 useCallback、useMemo 和 useEffect?

[英]When to use useCallback, useMemo and useEffect?

What is the main difference between useCallback , useMemo and useEffect ? useCallbackuseMemouseEffect之间的主要区别是什么?

Give examples of when to use each of them.举例说明何时使用它们中的每一个。

A short explanation.一个简短的解释。

useEffect使用效果

It's the alternative for the class component lifecycle methods componentDidMount , componentWillUnmount , componentDidUpdate , etc. You can also use it to create a side effect when dependencies change, ie "If some variable changes, do this".它是类组件生命周期方法componentDidMountcomponentWillUnmountcomponentDidUpdate等的替代方法。您还可以使用它来在依赖项更改时创建副作用,即“如果某些变量发生更改,请执行此操作”。

useCallback使用回调

On every render, everything that's inside a functional component will run again.在每次渲染时,功能组件内的所有内容都将再次运行。 If a child component has a dependency on a function from the parent component, the child will re-render every time the parent re-renders even if that function "doesn't change" (the reference changes, but what the function does won't).如果子组件依赖于父组件的函数,即使该函数“不会改变”(引用改变,但函数所做的不会改变),每次父组件重新渲染时,子组件也会重新渲染t)。
It's used for optimization by avoiding unnecessary renders from the child, making the function change the reference only when dependencies change.它用于通过避免来自子级的不必要渲染来进行优化,使函数仅在依赖项更改时更改引用。 You should use it when a function is a dependency of a side effect eg useEffect .当函数是副作用的依赖项时,您应该使用它,例如useEffect

useMemo使用备忘录

It will run on every render, but with cached values.它将在每次渲染上运行,但带有缓存值。 It will only use new values when certain dependencies change.它只会在某些依赖项更改时使用新值。 It's used for optimization when you have expensive computations.当您有昂贵的计算时,它用于优化。 Here is also a good answer that explains it .这也是解释它的一个很好的答案

useEffect() will let you create side effects on your components based on the dependencies you send to it. 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>

The example above is taken from the documentation of React .上面的例子取自 React 的文档 You can see that each time you click the button it will trigger an update on the count field (using setCount()) and then, the effect that depends on the count variable will trigger an update on the title of the page.您可以看到,每次单击按钮时,它都会触发计数字段的更新(使用 setCount()),然后,依赖于计数变量的效果将触发页面标题的更新。


useCallback() will return a memoized callback. useCallback()将返回一个记忆回调。 Normally, if you have a child component that receives a function prop, at each re-render of the parent component, this function will be re-executed;通常,如果您有一个接收函数 prop 的子组件,则在每次重新渲染父组件时,都会重新执行此函数; by using useCallback() you ensure that this function is only re-executed when any value on it's dependency array changes.通过使用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() will return a memoized value that is the result of the passed parameter. useMemo()将返回一个记忆值,该值是传递参数的结果。 It means that useMemo() will make the calculation for some parameter once and it will then return the same result for the same parameter from a cache.这意味着useMemo()将对某个参数进行一次计算,然后它将从缓存中为同一参数返回相同的结果。

This is very useful when you need to process a huge amount of data.当您需要处理大量数据时,这非常有用。

 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>

Most minimal explanation:最低限度的解释:

useEffect:使用效果:

Whenever you have some logic that is executed as reaction to a state change or before a change is about to happen.每当您有一些逻辑作为对状态更改的反应或在更改即将发生之前执行。

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

or in case of no dependency:或者在没有依赖的情况下:

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

useCallback:使用回调:

Whenever you have a function that is depending on certain states.每当你有一个依赖于某些状态的函数时。 This hook is for performance optimization and prevents a function inside your component to be reassigned unless the depending state is changed.这个钩子用于性能优化,并防止组件内的函数被重新分配,除非依赖状态发生变化。

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

Without useCallback, myFunction will be reassigned on every render.如果没有 useCallback,myFunction 将在每次渲染时重新分配。 Therefore it uses more compute time as it would with useCallback.因此,它使用更多的计算时间,就像使用 useCallback 一样。

useMemo使用备忘录

Whenever you have a value that is depending on certain state.每当您有一个取决于特定状态的值时。 Same as useCallback, useMemo is ment to reduce reassignments for performance optimization.与 useCallback 相同,useMemo 用于减少重新分配以优化性能。

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

Same as useCallback, myValue is only assigned when state is changing and therefore will reduce compute time.与 useCallback 相同,myValue 仅在状态改变时分配,因此会减少计算时间。 Otherwise myValue will be reassigned on every render.否则 myValue 将在每次渲染时重新分配。

!Trick to mimick componentWillMount lifecycle ! 模仿 componentWillMount 生命周期的技巧

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

Since useEffect is called after the first render and then on every dependency change.因为 useEffect 在第一次渲染之后调用,然后在每次依赖更改时调用。 It never runs before the first render.它永远不会在第一次渲染之前运行。 useMemo is executed inline with your JS therefore will be executed before it reaches your Components return statement. useMemo 与您的 JS 内联执行,因此将在它到达您的组件返回语句之前执行。

!NOTE : functions with useCallback and values with useMemo can be used as dependency in useCallback, useMemo and useEffect. !注意:带有 useCallback 的函数和带有 useMemo 的值可以用作 useCallback、useMemo 和 useEffect 中的依赖项。 It is highly recommended to use these hooks in order to have a well structured and readable flow of state in your component.强烈建议使用这些钩子,以便在您的组件中拥有结构良好且可读的状态流。 These hooks do not trigger a render.这些钩子不会触发渲染。 Only useState and useReducer do!只有 useState 和 useReducer 可以!

If you want to keep state that doesnt trigger a rerender or any of the above explained hooks you can use useRef.如果你想保持不触发重新渲染的状态或任何上述解释的钩子,你可以使用 useRef。 useRef will keep a value consistent over renders without triggering any state dependent value or effect. useRef将在渲染上保持一致的值,而不会触发任何状态相关的值或效果。

It's all well and good to know when to use the functions, but I wanted to know what the actual difference was between them!知道何时使用这些功能很好,但我想知道它们之间的实际区别是什么! Here is what I found:这是我发现的:

  • useMemo runs the code immediately, so the return value is available to code that comes after after it. useMemo立即运行代码,因此返回值可用于后面的代码。 This means it runs before the first render, so any useRef you are using to access HTML components won't be available on the initial run.这意味着它在第一次渲染之前运行,因此您用来访问 HTML 组件的任何useRef在初始运行时都将不可用。 (But you can add ref.current to the useMemo dependencies to have the useMemo code run again after the first render, when the useRef value has become available). (但是您可以将ref.current添加到useMemo依赖项中,以便在第一次渲染后再次运行useMemo代码,当useRef值可用时)。 Since the return value is available to code directly following it, this is why it is recommended for complex calculations that don't need to re-run on each render, as having the return value available immediately saves you from having to mess with the state to store the value now and access it later - just grab the return value of useMemo() and use it right away.由于返回值可用于直接跟随它的代码,这就是为什么建议用于不需要在每次渲染时重新运行的复杂计算的原因,因为返回值立即可用可以使您不必弄乱状态现在存储值并稍后访问它 - 只需获取useMemo()的返回值并立即使用它。
  • useEffect does not run immediately but runs after the first render. useEffect不会立即运行,而是在第一次渲染之后运行。 This means any useRef values referring to HTML elements will be valid on the first run.这意味着任何引用 HTML 元素的useRef值在第一次运行时都是有效的。 Since it runs after all the code in your function has finished and rendered, there is no point having a return value as there is no further code running that could use it.由于它在函数中的所有代码都完成并呈现后运行,因此没有返回值的意义,因为没有进一步运行的代码可以使用它。 The only way useEffect code can do anything visible is by either changing the state to cause a re-render, or modifying the DOM directly. useEffect代码可以执行任何可见操作的唯一方法是更改​​状态以导致重新渲染,或者直接修改 DOM。
  • useCallback is the same as useMemo except that it remembers the function itself rather than its return value. useCallbackuseMemo相同,除了它记住函数本身而不是它的返回值。 This means a useCallback function does not run immediately but can be run later (or not run at all), while useMemo runs its function immediately and just saves its return value for later use.这意味着useCallback函数不会立即运行,但可以稍后运行(或根本不运行),而useMemo立即运行其函数并只保存其返回值供以后使用。 Unlike useMemo this is not good for complex calculations as the code will run again every time it is used.useMemo不同,这不适用于复杂的计算,因为每次使用时代码都会再次运行。

For example, if we pass the same function to both useMemo and useCallback :例如,如果我们将相同的函数传递给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

Here, useCallback has remembered the function and will keep returning the original function on future renders until the dependencies change, while useMemo actually runs the function immediately and just remembers its return value.在这里, useCallback已经记住了该函数,并且会在未来的渲染中继续返回原始函数,直到依赖项发生变化,而useMemo实际上立即运行该函数并且只记住它的返回值。

Both useCallback() and useMemo() provide return values that can be used immediately, while useEffect() does not because its code does not run until much later, after the render has completed. useCallback()useMemo()提供可以立即使用的返回值,而useEffect()则不提供,因为它的代码要在渲染完成后很晚才运行。

useEffect

Gets called when the component mounts, unmounts and any of it's dependencies change.在组件挂载、卸载及其任何依赖项更改时调用。

Can be used to get data when component is mounted , subscribe and unsubscribe to event streams when component mounts and unmounts (think rxjs).可用于在组件mounted时获取数据,在组件mountsunmountssubscribeunsubscribe事件流(想想 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

Can also be used for onetime method call which require no cleanup也可用于不需要清理的一次性方法调用

useEffect(()=>{

  oneTimeData();

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


useCallback

  1. Got functions that you don't want to be re-created on every component render?有不想在每个组件渲染上重新创建的功能?

  2. Want a function that isn't called on component mount or unmount?想要一个在组件挂载或卸载时不被调用的函数?

Use useCallback使用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 to the rescue 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} />
 }
}

If the argument passed to setCount isn't a function, then the variables you would want useCallback to 'watch' out for must be specified in the dependencies array less there will be no change effect.如果传递给setCount的参数不是函数,那么您希望useCallback “注意”的变量必须在依赖项数组中指定,否则不会有任何更改效果。

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

useMemo

Doing heavy processing and want to memoize ( cache ) the results?进行繁重的处理并想要记忆缓存)结果? Use useMemo使用使用useMemo

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

All of these hooks have the same goal: avoiding redundant component rebuilds (and re-execution of the stuff inside the hooks).所有这些钩子都有相同的目标:避免冗余的组件重建(以及钩子内的东西的重新执行)。

useEffect returns nothing (void) and thus is suitable for such cases. useEffect返回任何内容(void),因此适用于此类情况。

useCallback returns a function which will be used later in the component. useCallback返回一个函数,稍后将在组件中使用该函数 Unlike normal function declaration, it will not trigger component rebuild unless its dependencies change.与普通函数声明不同,它不会触发组件重建,除非其依赖项发生变化。

useMemo is just another flavour of useCallback . useMemo只是useMemo的另一种useCallback

Here is the best explanation I've seen so far. 是迄今为止我见过的最好的解释。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM