Home > OS >  React: Children does not update when parent's value change
React: Children does not update when parent's value change

Time:11-05

I got it with Amire's answer


I have an array in store:

[
  {
    id: 1,
    value: 'value 1',
  },
]

In parent, I call it from store

And then, I transmit data to Child:

function Parent({array}){
  return
    <Child
      key={array}
      items={array}
    />
}

When I update array, delete or add item, then I get data again from backend to update, the prop in Child will be update. E.g:

[
  {
    id: 1,
    value: 'value 1',
  },
  {
    id: 2,
    value: 'value 2',
  },
]

But if I update:

[
  {
    id: 1,
    value: 'value 1',
  },
  {
    id: 2,
    value: 'value update',
  },
]

prop in parent is updated, but in child is not.

My ENG is not good but I wish that everyone will understand clearly my main idea.

CodePudding user response:

Reference to your array isn't changing, so React is doing a shallow comparison and decide that it doesn't need to re-render. Either implement shouldComponentUpdate logic yourself to explicitly check the elements of the array too, or just create a new array with the elements that you want and pass that to the component.

CodePudding user response:

As react doc says,

A “key” is a special string attribute you need to include when creating lists of elements.
The best way to pick a key is to use a string that uniquely identifies a list item among its siblings.

When you pass an array as a string representation to component key, (it most probably) gets converted to a string. meaning it will call the .toString method.

Let's see what Array.prototype.toString method do with your given array

const state = [
  { id: 1, value: 'value 1' },
  { id: 2, value: 'value 2' },
]

state.toString() // '[object Object],[object Object]'

const newState = [
  { id: 1, value: 'value 1' },
  { id: 5, value: 'value 5 ⭐' },
]
newState.toString() // '[object Object],[object Object]'
JSON.stingify(newState) // '[{"id":1,"value":"value 1"},{"id":5,"value":"value 5 ⭐"}]'

As you see your object will be converted to '[object Object]' and get joined by ,. this makes it clear why adding and deleting make triggers render but changing doesn't.

The better way to achieve this is to generate a deterministic hash to make it crystal clear for the react that mutation (changing) must trigger render. I provide a bunch of examples and you can pick what fits you.

// if id will change in your patches
key={array.map(item => item.id).join('-')}

// if id won't changes but title gets updated
// This is the probably the same as JSON.stringify
key={array.map(item => item.id item.title).join('-')}

// this example is not recommended but give you more insight
// I used combination of index and item id
key={array.map((item, idx) => `${idx}.${item.id}`).join(' ')}
// if your only the title changes this will not cause re-render

in-depth explanation on the negative impacts of using an index as a key.

If I suppose to do this I would probably remove the extra child layer and map over data directly in jsx.

function Parent({array}){
  return array && array.map(item => (
    <InnerChildComponent
      key={'get proper key from above list fit your case based on item'}
      items={item}
    />
  )
}
  • Related