Bishonen_PL
Bishonen_PL

Reputation: 1571

React - adding key interferes with state of children

having a parent element which requires a key (because it's in a list creating a warning message), I added key={uuid.v4()}. This made the message disappear.

Funny things started happening with it's child components, though. When I use the functional setState hook, it doesn't actually assign it to the value anymore (see below [1]). When I remove the key from the parent Component, the same code works (but leaves me with the warning). When adding a static key, e.g. key={'someComponent'}, the whole component doesn't render at all. Any tips what I'm missing here?

[1] Child Component which does not update it's state:

function zoomIntoRegion(countryName, filter) {
    props.changeFilter({SLMetric: filter})
    if (regionZoom === countryName && filter === props.filter) {
        setRegionZoom(undefined)
    } else {
        console.log('before setzoom', countryName) // # before setzoom GERMANY
        setRegionZoom(countryName)
        console.log('after', regionZoom) // # after undefined
    }
}

Upvotes: 1

Views: 47

Answers (1)

Agney
Agney

Reputation: 19224

Keys in React are used for quickly comparing the current children to the children before any change occured. When you set the key on a component to uuid.v4() in the render function, every time the parent component rerenders, you can see that it generates a new key. From the docs:

Keys should be stable, predictable, and unique. Unstable keys (like those produced by Math.random()) will cause many component instances and DOM nodes to be unnecessarily recreated, which can cause performance degradation and lost state in child components.

which seems to accurately define what you are facing.

To work around this problem,

  1. If you do not see the order of your children changing at all, then it is okay to use index as key.
  2. If you do see it changing and have no key in the data that you can use, then move the key generation setup to where you are fetching/generating the data so that it is executed only once. Ensure that keys do not change on render, but only when the data itself is changing.
function App() {
  const [items, setItems] = useState([]);
  useEffect(() => {
    getData()
      // mapping over array data and adding the key here
      .then((data) => data.map((item) => ({...item, id: uuid.v4() })))
      .then((data) => setItems(data))
  }, []);
  return (
    <Fragment>
      // using the key
      {items.map((item) => {
        <div key={item.id}>

        </div>
      })}
    </Fragment>
  )
}

Upvotes: 1

Related Questions