何时使用useCallback,useMemo和useEffect

Squ*_*chy 8 reactjs react-hooks

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

jpm*_*rks 19

最低限度的解释:

使用效果:

每当您有一些逻辑作为对状态更改的反应或在更改即将发生之前执行。

useEffect(() => {
  // execute when state changed
  () => {
    // execute before state is changed
  }
}, [state]);
Run Code Online (Sandbox Code Playgroud)

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

useEffect(() => {
  // execute when component has mounted
  () => {
    // execute when component will unmount
  }
}, []);
Run Code Online (Sandbox Code Playgroud)

使用回调:

每当你有一个依赖于某些状态的函数时。这个钩子用于性能优化,并防止组件内的函数被重新分配,除非依赖状态发生变化。

const myFunction = useCallback(() => {
  // execute your logic for myFunction
}, [state]);
Run Code Online (Sandbox Code Playgroud)

如果没有 useCallback,myFunction 将在每次渲染时重新分配。因此,它使用更多的计算时间,就像使用 useCallback 一样。

使用备忘录

每当您有一个取决于特定状态的值时。与 useCallback 相同,useMemo 用于减少重新分配以优化性能。

const myValue = useMemo(() => {
  // return calculated value
}, [state]); 
Run Code Online (Sandbox Code Playgroud)

与 useCallback 相同,myValue 仅在状态改变时分配,因此会减少计算时间。否则 myValue 将在每次渲染时重新分配。

! 模仿 componentWillMount 生命周期的技巧

useMemo(() => {
  // execute componentWillMount logic
]}, []);
Run Code Online (Sandbox Code Playgroud)

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

!注意:带有 useCallback 的函数和带有 useMemo 的值可以用作 useCallback、useMemo 和 useEffect 中的依赖项。强烈建议使用这些钩子,以便在您的组件中拥有结构良好且可读的状态流。这些钩子不会触发渲染。只有 useState 和 useReducer 可以!

如果你想保持不触发重新渲染的状态或任何上述解释的钩子,你可以使用 useRef。useRef将在渲染上保持一致的值,而不会触发任何状态相关的值或效果。


d4v*_*hez 8

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'))
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

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


useCallback()将返回一个记录的回调。通常,如果您有一个子组件接收一个函数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'));
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)


useMemo()将返回一个备注值,该值是传递的参数的结果。这意味着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'));
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)


Ven*_*sky 6

简短说明。

useEffect

这对类组件lifecicle方法(替代componentDidMountcomponentWillUnmountcomponentDidUpdate,...),你也可以用它作为基于一些依赖,“当某些变量的变化,这样做”的副作用。

useCallback

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

useMemo

它将在每个渲染器上运行,但是具有缓存的值。仅在某些依赖项更改时才使用新值。当您拥有昂贵的计算时,它用于优化。这也是解释它的好答案


Alf*_*Moh 6

useEffect

当组件安装、卸载及其任何依赖项发生更改时被调用。

mounted当组件为 时,可用于获取数据subscribe;当组件为 时unsubscribe,可用于获取事件流(想想 rxjs)。mountsunmounts

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
Run Code Online (Sandbox Code Playgroud)

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

useEffect(()=>{

  oneTimeData();

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

Run Code Online (Sandbox Code Playgroud)

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} />
 }
}

Run Code Online (Sandbox Code Playgroud)

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} />
 }
}
Run Code Online (Sandbox Code Playgroud)

如果传递给的参数setCount不是函数,那么您想要useCallback“监视”的变量必须在依赖项数组中指定,否则不会有任何更改效果。

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

useMemo

进行繁重的处理并想要记住缓存)结果?使用useMemo

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


Nik*_*lia 6

所有这些钩子都有相同的目标:避免冗余组件重建(以及重新执行钩子内的内容)。

useEffect不返回任何内容(void),因此适合这种情况。

useCallback返回一个稍后将在组件中使用的函数。与普通函数声明不同,除非其依赖项发生变化,否则它不会触发组件重建。

useMemo只是另一种味道useCallback

是迄今为止我见过的最好的解释。


Mal*_*ous 5

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

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

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

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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)

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

双方useCallback()useMemo()提供可立即使用的返回值,而useEffect()不会因为它的代码不能运行,直到很久以后,经过渲染完成。