简体   繁体   English

使用 useMemo 代替 React.memo 语法问题

[英]Using useMemo instead of React.memo syntax issue

I need to make a demonstration of using React Hooks useMemo.我需要演示如何使用 React Hooks useMemo。 I have working code that is as follows that does what I want:我有如下工作代码,可以满足我的要求:

const SpeakerCardDetail = React.memo(
  ({id,...

I found a link that shows that I could use syntax more like this but I can't figure it out exactly.我找到了一个链接,表明我可以使用更像这样的语法,但我无法完全弄清楚。

This is as far as I got:据我所知:

const SpeakerDetail = React.useMemo(() => {
   ({ id,

Clearly not it though.显然不是。 I do get that React.memo solves the problem but I do need to show useMemo in action and am hoping there is an alternative syntax that I can use.我确实知道 React.memo 解决了这个问题,但我确实需要展示 useMemo 的实际效果,并希望有另一种我可以使用的语法。

React.memo and React.useMemo are not equivalent at all (don't rely on naming similarity). React.memoReact.useMemo根本不等价(不要依赖命名相似性)。 Here's a quote from React.memo doc :这是React.memo doc 中的引用:

React.memo is a higher order component . React.memo是一个高阶组件

So it's a HOC that can optimize rendition of your component given that it renders the same output with the same properties.因此,它是一个 HOC,可以优化组件的呈现,因为它使用相同的属性呈现相同的输出。

React.useMemo on the other hand is more generic and returns a memoized value :另一方面, React.useMemo更通用并返回一个记忆值

Pass a “create” function and an array of dependencies.传递“创建”函数和依赖项数组。 useMemo will only recompute the memoized value when one of the dependencies (either a or b ) has changed. useMemo只会在依赖项之一( ab )发生变化时重新计算记忆值。

const memoizedValue = useMemo(
  () => computeExpensiveValue(a, b), 
  [a, b]
);

And while it can be hacked to be used instead of React.memo , it's not its purpose and it will add to the confusion more than it will help.虽然它可以被破解以代替React.memo ,但这不是它的目的,它只会增加混乱而不是帮助。 useMemo is a hook and is subject to the certain usage rules . useMemo是一个钩子,需要遵守一定的使用规则

And there's this warning as well:还有这个警告:

In the future, React may choose to “forget” some previously memoized values and recalculate them on next render, eg to free memory for offscreen components.将来,React 可能会选择“忘记”一些先前记忆的值并在下一次渲染时重新计算它们,例如为屏幕外组件释放内存。 Write your code so that it still works without useMemo — and then add it to optimize performance.编写您的代码,使其在没有useMemo情况下仍然可以工作——然后添加它以优化性能。

While memo is a HOC and useMemo is a hook, you can use them the achieve the same result. memo是一个 HOC 而useMemo是一个钩子,你可以使用它们来达到相同的结果。

For context, HOC is an older React pattern that has been used for many years with class-based and functional components alike.就上下文而言,HOC 是一种较旧的 React 模式,已在基于类和函数式组件中使用多年。 You can still use it today (there's no plan for deprecation).您今天仍然可以使用它(没有弃用的计划)。

Hooks is a relatively new concept (about a year) that enhances functional components and in many cases drastically simplifies code . Hooks 是一个相对较新的概念(大约一年),它增强了功能组件,并在许多情况下大大简化了代码 That's why many developers are moving towards using hooks.这就是为什么许多开发人员正在转向使用钩子的原因。

Anyway, both memo and useMemo take two arguments: a function and props.无论如何, memouseMemo接受两个参数:函数和道具。 If none of the props change on subsequent re-renders, the function is not executed again and instead returns the previous result.如果后续重新渲染时没有任何道具更改,则不会再次执行该函数,而是返回先前的结果。 This, in effect, replaces shouldComponentUpdate callbacks, with a purely functional approach.这实际上用纯函数式方法替换了shouldComponentUpdate回调。

With memo , your code would look like this:使用memo ,您的代码将如下所示:

const SpeakerCardDetail = React.memo(
  (props) => <div>{props.name}</div>
)

With useMemo , you'd write:使用useMemo ,你会写:

const SpeakerCardDetail = (props) => useMemo(() => <div>{props.name}</div>)

Notice that useMemo is used inside of your component function, while memo wraps the function.请注意, useMemo在您的组件函数内部使用,而memo包装了该函数。

More traditionally, useMemo could be written as:更传统地, useMemo可以写成:

function SpeakerCardDetail(props) {
  return useMemo(
    () => <div>{props.name}</div>
  )
}

Now, the code above would re-render every time, making the useMemo function a bit useless.现在,上面的代码每次都会重新渲染,使得useMemo函数有点无用。 To make it work its magic, we need to add the second argument.为了让它发挥它的魔力,我们需要添加第二个参数。 ( memo still works even without specifying the second argument but you can add it to customize it) (即使不指定第二个参数, memo仍然有效,但您可以添加它来自定义它)

There's a slight difference in the format of the second argument.第二个参数的格式略有不同。 memo expects a function that compares previous and current props, just like shouldComponentUpdate does for class components. memo需要一个函数来比较之前和当前的 props,就像shouldComponentUpdate对类组件shouldComponentUpdate一样。

const SpeakerCardDetail = React.memo(
  (props) => <div>{props.name}</div>
,
  // return true if passing nextProps to render would return the same result as passing prevProps to render, otherwise return false
  (prevProps, nextProps) => prevProps.name === nextProps.name
)

useMemo , on the other hand, expects an array as the second argument.另一方面, useMemo需要一个数组作为第二个参数。 Whenever the values in the array change, the function would be executed again.每当数组中的值发生变化时,该函数将再次执行。

function SpeakerCardDetail(props) {
  return useMemo(
    () => <div>{props.name}</div>
  ,
    [props.name]
  )
}

There's really no more magic than that.真的没有比这更神奇的了。 Both memo and useMemo are used to memoize the result of a function, the only difference is memo is a HOC (and can be used to wrap both class and functional components) which useMemo is a hook (and can only be used inside functional components). memouseMemo都用于useMemo函数的结果,唯一的区别是memo是一个 HOC(并且可以用于包装类和功能组件)而useMemo是一个钩子(并且只能在功能组件内部使用) .

To summarise React.memo vs useMemo / TLDR:总结React.memouseMemo / TLDR:

React.memo is a higher-order component (HOC for short) that will memoize a react component based on the props. React.memo是一个高阶组件(简称 HOC),它会根据 props 记忆一个 react 组件。

export function SomeComponent({ num }) {
  return <p>{num * 10}</p>
}

export default React.memo(SomeComponent, function areEqual(
  prevProps,
  nextProps
) {
  if (prevProps.num !== nextProps.num) {
    return false
  }
  return true
})

useMemo is a react hook that will memoize the value that is returned from the function you provide it. useMemo是一个反应钩子,它会useMemo你提供的函数返回的值。

export function SomeComponent({ num }) {
  const res = useMemo(() => num * 10, [num])
  return <p>{res}</p>
}

Source 来源

React.Memo反应备忘录

Using React.memo will cause React to skip rendering a component if its props have not changed.如果组件的 props 没有改变,使用React.memo将导致 React 跳过渲染组件。

Example:例子:

const Child = React.memo(props => {
  console.log("rendered");
  return <React.Fragment>{props.name}</React.Fragment>;
});

class App extends React.Component {
  state = {
    value: 1,
    name: "Jioke"
  };

  handleClick = () => {
    this.setState({
      value: this.state.value + 1
    });
  };

  render() {
    return (
      <React.Fragment>
        <Child name={this.state.name} />
        <div>{this.state.value}</div>
        <button onClick={this.handleClick}>+</button>
      </React.Fragment>
    );
  }
}

When we click button, Child Component does not re-render( rendered is displayed only 1)当我们点击按钮时, Child组件不会重新渲染( rendered只显示1个)

Notes: The more props, the more calculations, comparison(check is render or not) added comparison cost is not worth it for a "simple" component in terms of render, reconcile, DOM change and side-effect costs.注意:道具越多,计算越多,比较(检查是否渲染)增加的比较成本对于“简单”组件在渲染,协调,DOM更改和副作用成本方面是不值得的。 So be careful to decide use it or not所以要小心决定是否使用它

UseMemo使用备忘录

useMemo will cache a value so that it does not need to be recalculated each times components is re-render. useMemo将缓存一个值,以便每次重新渲染组件时都不需要重新计算。 It saves return value of function and returns if the inputs are not changed.它保存函数的返回值,如果输入不变则返回。

Example:例子:

import { useState, useMemo } from "react";
import ReactDOM from "react-dom";

const App = () => {
  const [count, setCount] = useState(0);
  const [todos, setTodos] = useState([]);
  const calculation = useMemo(() => expensiveCalculation(count), [count]);

  const increment = () => {
    setCount((c) => c + 1);
  };
  const addTodo = () => {
    setTodos((t) => [...t, "New Todo"]);
  };

  return (
    <div>
      <div>
        <h2>My Todos</h2>
        {todos.map((todo, index) => {
          return <p key={index}>{todo}</p>;
        })}
        <button onClick={addTodo}>Add Todo</button>
      </div>
      <hr />
      <div>
        Count: {count}
        <button onClick={increment}>+</button>
        <h2>Expensive Calculation</h2>
        {calculation}
      </div>
    </div>
  );
};

const expensiveCalculation = (num) => {
  console.log("Calculating...");
  for (let i = 0; i < 1000000000; i++) {
    num += 1;
  }
  return num;
};

ReactDOM.render(<App />, document.getElementById('root'));

we have an expensive function that runs on every render.我们有一个在每次渲染上运行的昂贵函数。 When changing the count or adding a todo, you will notice a delay in execution.更改计数或添加待办事项时,您会注意到执行延迟。 So when we use useMemo the expensive function will only run when its dependencies have changed.因此,当我们使用useMemo ,昂贵的函数只会在其依赖项发生变化时运行。 In the following example, the expensive function will only run when count is changed and not when todo's are added.在下面的例子中,昂贵的函数只会在计数改变时运行,而不会在添加待办事项时运行。

Please use this simple example to better understand the answers above.请使用这个simple的例子来更好地理解上面的答案。

// index.js

import React , { useState, useMemo } from 'react';
import ReactDOM from 'react-dom/client';

const Child1 = (props) => {
  console.log("Child1");
  return <p>Child 1</p>;
};

const Child2 = React.memo((props) => {
  console.log("Child2");
  return <p>Child 2</p>;
});

const Child3 = (props) => {
  console.log("Child3");
  return <p>Child 3</p>;
};

const expensiveCalculation = (label) => {
  console.log(label);
  return label;
}; 

function App() {
  console.log("App");

  const [count, setCount] = useState(0);
  const child3 = useMemo(() => <Child3 />, []);

  const calculation1 = expensiveCalculation("Expensive calculation without useMemo");
  const calculation2 = useMemo(() => expensiveCalculation("Expensive calculation with useMemo"), []);

  return (
    <>
      <button onClick={() => {setCount(c => c + 1);}}>Increase count</button>
      <p>Current count: {count}</p>

      <Child1 />
      <Child2 />
      {child3}

      <p>{calculation1}</p>
      <p>{calculation2}</p>
    </>
  );
};

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

Input/output:输入输出:

// Initial render
App
Expensive calculation without useMemo
Expensive calculation with useMemo
Child1
Child2
Child3
// Click on the "Increase count" button and observe the impact of memo and useMemo.
App
Expensive calculation without useMemo
Child1

Notes:笔记:

  • I have taken a very simple example to make it easy to explain.我举了一个非常简单的例子,以便于解释。 Please research a little bit more on the arguments memo and useMemo takes.请对 arguments 备忘录和 useMemo 进行更多研究。

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

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