0

I have a component which passes an object as a prop to its children, as well as a function to update this object. Now, I've noticed in one of the children that reading a nested property of the prop (obj.nested.property) object always returns the initial value as it is on mount, although the prop (obj) is updated successfully - I can see this in react dev tools, and also from the useEffect console.log

Here's the structure:

const Parent = () => {
  const obj = useSelector(config);
  const setObj = (newObj) => {
    // update obj code
  }

  return (
    <Child obj={obj} onUpdate={setObj}/>
  )
}

const Child = ({ obj, onUpdate }) => {
  useEffect(() => {
    console.log(obj.nested.property);
    // here everything is correct. Every time the obj is updated, this logs the new 
    // obj.nested.property value
  }, [obj])

  const onBlur = (newValue) => {
    if (newValue !== obj.nested.property) {
      // here, after you change the value once, and then just click inside the input
      // and blur, this condition is always true, because obj.nested.property keeps the
      // value as it was on mount
      onUpdate(newValue)
    }
  }

  return (
    <Input value={obj.nested.property} onBlur={onBlur}/>
  )
}
Marina I.
  • 57
  • 6
  • `onBlur` takes an event as an argument, are you storing events in your object? Otherwise the condition will always be true. – Guillaume Brunerie Sep 16 '22 at 18:39
  • And what do you mean by "update obj code"? Do you mean doing something like `obj.nested.property = someValue`, or `dispatch(someAction)` (assuming you use Redux) – Guillaume Brunerie Sep 16 '22 at 18:41

2 Answers2

0

Did you try to make a useState with that object?

const Child = ({ propObj as obj, onUpdate }) => {
  
  const [obj,setObj]= useState(propObj);

  useEffect(() => {
    console.log(obj.nested.property);
    // here everything is correct. Every time the obj is updated, this logs the new 
    // obj.nested.property value
  }, [obj])

  const onBlur = (newValue) => {
    if (newValue !== obj.nested.property) {
      // here, after you change the value once, and then just click inside the input
      // and blur, this condition is always true, because obj.nested.property keeps the
      // value as it was on mount
      onUpdate(newValue)
    }
  }

  return (
    <Input value={obj.nested.property} onBlur={onBlur}/>
  )
}
dongnez
  • 1
  • 2
  • I can, moreover this bug doesn't even break the UI :) I just want to understand why this is happening – Marina I. Sep 14 '22 at 14:38
  • I really don't know, i have this problem sometimes and i just solve it this way :) (I don't know if its possible to contact and deep in this problem somewhere else) – dongnez Sep 15 '22 at 07:57
0

According to react documentation, react doesn't update state immediately after calling setState function(because setState is async). new value will set in state after re-rendering the component(here is Child re-render). React documentation says:

setState() does not immediately mutate state but creates a pending state transition. Accessing state after calling this method can potentially return the existing value. There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.

therefore, immediately after calling component setState function and updating state(before re-rendering of component), the value of state is previous value. but in useEffect that calls after re-rendering of component, state has new value. You can see this for more info.