Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spreading props in react while maintaining reference equality

Imagine that I have a function which dynamically generates some of a component's props, and I want to pass them all at once without being explicit about every prop the function could generate. Normally you can do this with the spread operator, but the issue with the spread operator is that it created a new object each time. This would mean that (if I understand correctly) during the React Reconciliation, the component would have new props every time and would rerender every time, even if the props the function generated are the same.

Here's a concrete example:

const generateProps = () => ({foo: 'bar'});

const ParentComponent = () => ({
   const someProps = generateProps();

   return (
      <SomeComponent><ChildComponent {...someProps} otherProp='hello world'/></SomeComponent>
   )
})

Here ChildComponent would render every time ParentComponent would render (right?). One thing I know you could do is wrap the ChildComponent with a React.memo and do a deeper comparison of the props (passing a custom comparison function to it), but what if you don't have control over ChildComponent? Are you forced into being explicit? Or am I incorrect and ChildComponent wouldn't rerender in this example (assuming ChildComponent simply consumes the props and doesn't use any contexts or anything).

Thank you!

like image 466
Eli Avatar asked Oct 29 '25 01:10

Eli


1 Answers

You have it wrong. Reconciliation doesn't look at the props. It mainly looks at the component type, e.g.

if on one render you render

<Comp1/>

and on next render, on the same place in the component tree, you render:

<Comp2/>

it will unmount Comp1 and mount Comp2 because the type of components is different. If component types are the same, it will update existing one. There are some more details but you can check them yourself.

Furthermore, the props are also compared in a shallow way by default if you use React.memo, so if on one render you pass

let y = {a:1,b:2};
....
<Comp1 {...y}/>

and on next render you pass

let x = {a:1,b:2};
...
<Comp1 {...x}/>

Default comparison of React.memo will assume that props didn't change, because a and b have same values. You can verify here, clicking on the div doesn't re render the Test component:

 

let Test = React.memo(props => {
  console.log(props);
  return <div>{props.a}</div>;
});

function App() {
  let [state, setState] = React.useState({ a: 123 });
  return (
    <div
      onClick={() => {
        setState({ a: 123 });
      }}
    >
      <h1>Hello StackBlitz!</h1>
      <Test {...state} />
      <p>Start editing to see some magic happen :)</p>
    </div>
  );
}
ReactDOM.render(
    <App  />,
    document.getElementById("react")
);
  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
  <div id="react"></div>
like image 154
Giorgi Moniava Avatar answered Oct 31 '25 01:10

Giorgi Moniava



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!