Why call a simple trick to optimize react rerendering

There is no use of useMemo or useCallback in this optimization, just a little trick

Here’s an example:

A component that costs performance

import React from 'react';

function ExpensiveRender() { 
    const now = performance.now(); 
    while(performance.now() - now < 100) { 
        // Simulate a delay
    } 
    console.log("Rendered a component with delay."); 
    return <h3>ExpensiveRender components</h3>; 
} 



function App() {
    const [color, setColor] = React.useState("red");

    return (
        <div>
          <input type="text" value={color} onChange={(e)= > setColor(e.target.value)} />
          <p style={{color: color}} >Hello, World!</p>
          <ExpensiveRender />
        </div>
    );
}
Copy the code

ExpensiveRender />









The diagram below:

Obviously, that’s not what we want to see

The first method: Memo

Although the memo method is not used to solve this problem, it is still possible to write it down

Change the previous code to the following:

import { useState, memo } from "react";

// The memo is used here
const ExpensiveRender = memo (() = > { 
    const now = performance.now(); 
    while (performance.now() - now < 100) { 
        // Simulate a delay
    } 

    console.log("Rendered a component with delay."); 
    return <h3>ExpensiveRender components</h3>; 
}); 



function App() {
    const [color, setColor] = useState("red");

    return (
        <div>
          <input
            type="text"
            value={color}
            onChange={(e)= > setColor(e.target.value)}
          />
          <p style={{ color: color}} >Hello, World!</p>
          <ExpensiveRender />
        </div>
    );
}
Copy the code

Add a Memo method to the
component

The effect is shown below:

Mm-hmm. That solves the problem

However, the Memo method is not the main focus of this blog post

Second method: Separate the operation that changes State

Looking at the performance-wasting component code again, it’s not hard to see that only some of the components really need to be updated

function App() {
  const [color, setColor] = useState("red");

  return (
    <div>/************* This part start *************/<input
        type="text"
        value={color}
        onChange={(e)= > setColor(e.target.value)}
      />
      <p style={{ color: color}} >Hello, World!</p>/************* End *************/<ExpensiveRender />
    </div>
  );
}

Copy the code

So we can isolate this part as a component so that it doesn’t affect the other components

Theory holds, practice begins

import { useState } from "react";

// Component with delay
function ExpensiveRender() {
  const now = performance.now();
  while (performance.now() - now < 100) {
    // Simulate a delay
  }

  console.log("Rendered a component with delay.");
  return <h3>ExpensiveRender components</h3>;
}

// Change the color component
function Form() {
  const [color, setColor] = useState("red");

  return (
    <>
      <input
        type="text"
        value={color}
        onChange={(e)= > setColor(e.target.value)}
      />
      <p style={{ color: color}} >Hello, World!</p>
    </>
  );
}

function App() {
  return (
    <div>
      <Form />
      <ExpensiveRender />
    </div>
  );
}
Copy the code

If the color changes, the

component will only be re-rendered. This does not affect the < siverender /> component

So you can see that the same effect can be achieved

Third method: content enhancement

You may think the second method is enough, but it’s not a panacea

For example, an effect needs to be applied to multiple components, including components that cost performance

Such as:

function App() {
  const [color, setColor] = useState("red");

  return (
    <div style={{ color: color}} >
      <input
        type="text"
        value={color}
        onChange={(e)= > setColor(e.target.value)}
      />
      <p>Hello, World!</p>
      <ExpensiveRender />
    </div>
  );
}
Copy the code

This doesn’t seem like an easy way to separate out the part that operates on state

Does this seem to require the Memo method again? No, there’s another way

The code:

const ColorComponent: React.FC = ({ children }) = > {
  const [color, setColor] = useState("red");

  return (
    <div style={{ color: color}} >
      <input
        type="text"
        value={color}
        onChange={(e)= > setColor(e.target.value)}
      />
      {children}
    </div>
  );
};

function App() {
  return (
    <ColorComponent>
      <p>Hello, World!</p>
      <ExpensiveRender />
    </ColorComponent>
  );
}

Copy the code

That can also solve the problem

Why is that?

=
=
=
=
=
=
=
=
=
However, this part is still passed to the
component as the children property

When < ColorComponent / > in the component color change, of course, this component will be rendered, but there is no change, children attribute value before it is not change, so do not access to the children property, So it doesn’t affect children

That being said, don’t be too stubborn. Not only the children property works, but the properties in props also work

As follows:

interface IProps {
  label: React.ReactNode;
}

const ColorComponent: React.FC<IProps> = ({ label, children }) = > {
  const [color, setColor] = useState("red");

  return (
    <div style={{ color: color}} >
      <input
        type="text"
        value={color}
        onChange={(e)= > setColor(e.target.value)}
      />

      {label}
    </div>
  );
};

function App() {
  return <ColorComponent label={<ExpensiveRender />} ></ColorComponent>;
}

Copy the code

This is also ok, the effect is the same, the principle is the same

The last

It’s a natural consequence of React composition model. It’s simple enough that It’s underappreciated, and deserves a bit more love.

– Dan

Yes, they’re nothing new, but keep an eye out for them, as “high-end ingredients often require only the most understated cooking.”

Comments are welcome if there are any mistakes

Reference links:

  • Before You memo()
  • One simple trick to optimize React re-renders