6

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

geocodezip
  • 158,664
  • 13
  • 220
  • 245
stanleyxu2005
  • 8,081
  • 14
  • 59
  • 94

1 Answers1

3

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?

Dennis Vash
  • 50,196
  • 9
  • 100
  • 118
  • I take your point. I think my major wrong is that, I consider stateless component is same as pure component. But they actually behave quite differently. – stanleyxu2005 Jul 17 '19 at 15:30
  • But you know, in a large project, some people (like me) can be wrong to write shitty code. My wish is even if I write something like `new String(...)`, I can still find the problem at static-checking stage, or use some built-in/3rd-party content level dirty checks to help. – stanleyxu2005 Jul 17 '19 at 15:34
  • 1
    Check [why-did-you-render library](https://github.com/welldone-software/why-did-you-render). – Dennis Vash Jul 17 '19 at 15:35