Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid unnecessary re-rendering of React Component

I've been learning new features of React 16.8. I believe React's Pure Component should automatically avoid unnecessary re-render operations.

In the following example, the App itself is a stateless component. I use useState to maintain two state objects text and nested: {text}.

There are 3 tests. The first 2 tests work. No matter how many times, I change the state, no re-render operation will be required.

Now, the third test tries to set the state of text with the same string value, but the reference is different. I expect nothing to be re-rendered, but actually, the <Headline/> will be re-rendered.

Shall I use certain memorize technique to avoid? I feel it will be too much code to archive that. And programmer must be very careful to write high-quality React code. ..

class Headline extends React.PureComponent {
  render() {
   const {text} = this.props;
   return <h1>{text} (render time: {Date.now()})</h1>;
  }
} 

const simpleText = 'hello world'

const App = () => {
  const [text, setText] = React.useState(simpleText)
  const [nested, setNested] = React.useState({text: simpleText})
  return (
    <div>
      <Headline text={text}/>
      <Headline text={nested.text}/>

      <button onClick={()=>setText(simpleText)}>
        test 1: the first line should not change (expected)
      </button>

      <button onClick={()=>setNested({text: simpleText})}>
        test 2: the second line will not change  (expected)
      </button>

      <button onClick={()=>setText(new String(simpleText))}>
        test 3: the first line will change on every click (why?)
      </button>
    </div>
  )
}

ReactDOM.render(<App />, document.querySelector("#app"))

Here is a live playground in jsfiddle:

https://jsfiddle.net/fL0psxwo/1/

Thank you React folks, cheers!


Update 1: Thanks Dennis for mentioning why-did-you-render

The author points some very useful articles. I think it could be very educational to everybody. https://medium.com/welldone-software/why-did-you-render-mr-big-pure-react-component-part-2-common-fixing-scenarios-667bfdec2e0f

Update 2: I created a new hook called withDirtyCheck so that my code will automatically do content dirty check.

import isEqual from 'lodash-es/isEqual';

export const withDirtyCheck = ([getter, setter]) => {
  const setStateIfDirty = (nextState) =>
    setter((prevState) => (isEqual(prevState, nextState) ? prevState : nextState));

  return [getter, setStateIfDirty];
};

Checkout my latest library https://github.com/stanleyxu2005/react-einfach

like image 780
stanleyxu2005 Avatar asked Jul 17 '19 14:07

stanleyxu2005


1 Answers

The problem is that with new operator you creating a String Object which is always different from the previous state.

'hello world' === new String('hello world') // false, always.
'hello world' === String('hello world')     // true

Check this example:

setText(prevState => {

  // Will render
  // const currState = new String(simpleText);

  // Won't render
  const currState = String(simpleText);

  console.log(prevState === currState); // if true, no re-render
                                        // if false, re-render

  return currState;
});

Edit antd-starter

Refer to What is the difference between string primitives and String objects in JavaScript?

like image 50
Dennis Vash Avatar answered Oct 29 '22 17:10

Dennis Vash