Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

new Date as react key prop?

I have this react code where i am using new Date().getTime() as react key props on some Input components.This is probably an antipattern because keys need to be stable. But i want to understand why is this so buggy. And why newDate().getTime() as key behaves worse than Math.random() as key.Please check these 2 examples out to see what i mean:

  • example with Math.random() as key
  • example with new Date().getTime() as key

The kind of code in question:

class TextInputs extends React.Component {
  state = {
    textArray: ['hi,','My','Name','is']
  };
  handleChange = ({value, index}) => {
    const {textArray} = this.state;
    textArray[index] = value;
    this.setState({textArray})
  };
  render(){
    console.log(this.state.textArray)
  return this.state.textArray.map((txt, i) => <Input onChange={this.handleChange} index={i} value={txt} key={new Date().getTime()}/>)               
  };
};
like image 629
Shady Pillgrim Avatar asked Jul 25 '18 17:07

Shady Pillgrim


1 Answers

This is probably an antipattern because keys need to be stable.

Indeed. :-)

But i want to understand why is this so buggy. And why newDate().getTime() as key behaves worse than Math.random() as key.

Because you can render lots of elements within the same millisecond. A millisecond is a very short time to humans. To computers, not so much. So you end up with keys that aren't unique.

Before you (or anyone reading this) is tempted to reach for performance.now() instead, remember that as you said, keys need to be stable, and also performance.now()'s accuracy opened the door to some attacks and so implementations have backed off (it's still really precise, but not accurate to the 5 microseconds it was originally supposed to be).


Side note: This code is wrong:

handleChange = ({value, index}) => {
  const {textArray} = this.state;
  textArray[index] = value;
  this.setState({textArray})
};

There are two problems with that code, both described on this page in the documentation:

  1. You're modifying state directly, when assigning to the array entry. You must copy the array and modify the copy.

  2. You're using the non-callback version of setState to set state based on existing state. When setting state based on existing state, you must use the callback version of setState.

Ideally, don't use array indexes for this at all; use a unique ID on the object. But if you use an index, the correct code would be:

handleChange = ({value, index}) => {
  // Remember the string we're supposed to remove
  const entry = this.state.textArray[index];
  this.setState(({textArray}) => { // <== Note destructuring
      // Find the first occurrence in the array as it is NOW (it may have changed)
      const index = textArray.indexOf(entry);
      // If the entry is still there...
      if (index != -1) {
          // ...create a copy of the array without it, return the state update
          return {textArray: textArray.filter((e, i) => i !== index)};
          // Or:
          // textArray = textArray.slice();
          // textArray.splice(index, 1);
          // return {textArray};
          // Or:
          // return {textArray: [...textArray.slice(0, index), ...textArray.slice(index + 1)]};
      }
  });
};
like image 160
T.J. Crowder Avatar answered Oct 24 '22 19:10

T.J. Crowder