Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React.memo - why is my equality function not being called?

Tags:

I have a parent component that renders a collection of children based on an array received via props.

import React from 'react'; import PropTypes from 'prop-types'; import shortid from 'shortid'; import { Content } from 'components-lib'; import Child from '../Child';  const Parent = props => {   const { items } = props;    return (     <Content layout='vflex' padding='s'>       {items.map(parameter => (         <Child parameter={parameter} key={shortid.generate()} />       ))}     </Content>   ); };  Parent.propTypes = {   items: PropTypes.array };  export default Parent; 

Every time a new item is added, all children are re-rendered and I'm trying to avoid that, I don't want other children to be re-rendered I just want to render the last one that was added.

So I tried React.memo on the child where I'll probably compare by the code property or something. The problem is that the equality function never gets called.

import React from 'react'; import PropTypes from 'prop-types'; import { Content } from 'components-lib';  const areEqual = (prevProps, nextProps) => {   console.log('passed here') // THIS IS NEVER LOGGED!! }  const Child = props => {   const { parameter } = props;   return <Content>{parameter.code}</Content>; };  Child.propTypes = {   parameter: PropTypes.object };  export default React.memo(Child, areEqual); 

Any ideas why?

like image 960
Joana Deluca Kleis Avatar asked Nov 22 '19 14:11

Joana Deluca Kleis


People also ask

How is React memo different from useMemo?

memo is for when you find an existing component that is expensive to render, and you don't have the option to optimize it internally. Just wrap it and let React do an extra check. React. useMemo is for internally optimizing a component by saving the return value of an expensive function call.

Does React memo shallow comparison?

Custom Prop Equality: areEqual()By default, React. memo() performs a shallow comparison of props and prop objects. We can then pass this function as a second argument in React.

Is React memo a hoc?

React. memo() is a HOC that memoizes the passed in component. Doing so helps in optimizing its performance by preventing unnecessary re-renders due to state changes it does not depend on, e.g. those coming from ancestor components.

Should I wrap every component in React memo?

React.memo is a higher order component. If your component renders the same result given the same props, you can wrap it in a call to React.memo for a performance boost in some cases by memoizing the result. This means that React will skip rendering the component, and reuse the last rendered result.


2 Answers

In short, the reason of this behaviour is due to the way React works.

React expects a unique key for each of the components so it can keep track and know which is which. By using shortid.generate() a new value of the key is created, the reference to the component changes and React thinks that it is a completely new component, which needs rerendering.

In your case, on any change of props in the parent, React will renrender all of the children because the keys are going to be different for all of the children as compared to the previous render.

Please reference this wonderful answer to this topic

Hope this helps!

like image 80
Konstantin Avatar answered Oct 01 '22 02:10

Konstantin


I was having the same issue and the solution was a really dumb one. I'm posting this just in case if someone made the same mistake.

If your memo function does have useState state variable make sure that you pass it as a prop. Your memo function also has to be outside of your outer function. So instead of:

function App() {   const [strVar, setStrVar] = useState("My state str");    const MyElement = React.memo(({textProp}) => {     return (       <Text> {textProp + "\n" + strVar} </Text>     )   }, (prevProps, nextProps) => {       console.log("Hello"); //Never called   });    return (     <MyElement textProp= {"str"}/>   ) } 

Do it like this:

const MyElement = React.memo(({textProp, strVar}) => {   return (     <Text> {textProp + "\n" + strVar} </Text>   ) }, (prevProps, nextProps) => {    console.log("Hello"); });   function App() {   const [strVar, setStrVar] = useState("My state str");    return (     <MyElement textProp = {"str"} strVar = {strVar}/>   ) } 
like image 32
Otto Ranta-Ojala Avatar answered Oct 01 '22 03:10

Otto Ranta-Ojala