Using useMemo instead of React.memo syntax issue
Asked Answered
M

6

49

I need to make a demonstration of using 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.

Mongo answered 2/4, 2019 at 2:28 Comment(3)
wait what is it that you need to do? just any example of using useMemo ?Twinberry
React.memo allows functional component to have same optimization as PureComponent provides for class-based components. useMemo is for memoizing function's calls inside functional component. It would be hard to replace one with another since they have different purposes.Kathrinekathryn
I'm hoping to be able to get the same result as React.memo, which is that the return of the pure function is saved with memoization.Mongo
F
66

React.memo and React.useMemo are not equivalent at all (don't rely on naming similarity). Here's a quote from React.memo doc:

React.memo is a higher order component.

So it's a HOC that can optimize rendition of your component given that it renders the same output with the same properties.

React.useMemo on the other hand is more generic and returns a memoized value:

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.

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. useMemo is a hook and is subject to the certain usage rules.

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, e.g. to free memory for offscreen components. Write your code so that it still works without useMemo — and then add it to optimize performance.

Frugal answered 2/4, 2019 at 5:3 Comment(2)
Thanks. I've come to the same conclusion and was fooled by the naming. I'm using as my example now the resuilts of a sort before rendering.Mongo
Though, I have to say I stared at this example for a long time because it makes them feel somewhat the same. codesandbox.io/s/7m03xn9wlxMongo
R
41

While memo is a HOC and useMemo is a hook, you can use them the achieve the same result.

For context, HOC is an older React pattern that has been used for many years with class-based and functional components alike. 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. That's why many developers are moving towards using hooks.

Anyway, both memo and useMemo take two arguments: a function and props. 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.

With memo, your code would look like this:

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

With useMemo, you'd write:

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

Notice that useMemo is used inside of your component function, while memo wraps the function.

More traditionally, useMemo could be written as:

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. 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)

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.

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. 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).

Ritualize answered 11/1, 2020 at 23:43 Comment(4)
Could you please suggest us which is better in way we can use both approach?Wolfgang
@Wolfgang Neither is strictly better than the other. If you use class components then you have to use memo. If you use functional components with hooks, you might want to use useMemo (though, in this case, you could use memo, too). useMemo also has a more concise second argument. In the end, it's all just a matter of preference. Any difference in performance should be negligible.Ritualize
When the component is inside a condition, with useMemo, you get the rendered fewer hooks than expected, so in that case I suppose memo is the only option.Dardani
The bottom line is, if you're comfortable with using hooks and you use them throughout your project, you might prefer useMemo for "consistency". Though, as @GoranJakovljevic notes, regular hooks rules apply, so you can always fall back to React.memo. If you don't use hooks then just always go with React.memo.Ritualize
D
6

To summarise React.memo vs useMemo / TLDR:

React.memo is a higher-order component (HOC for short) that will memoize a react component based on the props.

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.

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

Source

Dyanne answered 26/11, 2020 at 12:49 Comment(0)
J
1

React.Memo

Using React.memo will cause React to skip rendering a component if its props have not changed.

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)

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. 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. 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. In the following example, the expensive function will only run when count is changed and not when todo's are added.

Jacobinism answered 20/11, 2021 at 9:22 Comment(0)
I
0

Please use the below simple example to better understand the answers above. The example demonstrates these scenarios in the simplest way:

  • memo
    • If the props do not change, the component won't be re-rendered.
  • useMemo
    • If the dependencies do not change, the component won't be re-rendered.
    • If the dependencies do not change, the function (useful for expensive functions) won't be re-run.
// 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.
Impunity answered 7/9, 2022 at 13:58 Comment(0)
G
0

In new react documentation said that you can use useMemo instead of memo https://react.dev/reference/react/useMemo#memoizing-individual-jsx-nodes

export default function TodoList({ todos, tab, theme }) {
      const visibleTodos = useMemo(
          () => filterTodos(todos, tab), [todos, tab]
      );
      const children = useMemo(
          () => <List items={visibleTodos} />, [visibleTodos]
      );
      return (
        <div className={theme}>
          {children}
        </div>
      );
}

But, on documentation page you can found:

Manually wrapping JSX nodes into useMemo is not convenient. For example, you can’t do this conditionally. This is usually why you would wrap components with memo instead of wrapping JSX nodes.

And it's good to remember that memo has a second optional parameter which can be used to compare props and and in some cases re-render component in some not:

memo(Component, arePropsEqual?)

https://react.dev/reference/react/memo#memo

Grecize answered 20/7, 2023 at 21:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.