0

I have a state declaration in my application that holds an array of information and can be updated by a form nested within a sub-component.

I'm using useStates callback syntax in order to update the data, but for some reason, despite the callback running and the state changing, it won't be updated.

This can be confirmed when checking a state change in useEffect; it won't output any changes with a console.log. However, inside the callback itself, the console.log will work every time (so clearly the callback is running).

This is a simplified layout of what I'm doing:

const MainContext = createContext();

const Parent = () => {
    const [data, setData] = useState([]);

    const updateData = (index, field, value) => {
        // This just updates (or initialises) an object at the specified index.
        setData(previousState => {
            let candidates = previousState;
            let candidate = candidates[index];

            if(typeof candidate !== "object"){
                candidate = {};
            }

            candidate[field] = value;
            candidates[index] = candidate;
      
            // This will run every time, and return the correct result:
            // [{
            //     example: value
            // }]
            console.log(candidates);
            return candidates;
        });
    };

    useEffect(() => {
        console.log(data); // This will not run whenever the field is updated.
    }, [data]);
 
    return (
        <MainContext.Provider value={{
            dispatch: {
                updateData
            }
        }}>
            <Child />
        </MainContext.Provider>
    );
};


// Whenever a user enters data inside this components input, it should push a change
// via the dispatch.updateData method and update the array in the parent component.
const Child = () => {
    const testIndex = 1;
    return (
        <MainContext.Consumer>{({ dispatch }) => (
            <input 
                name="example" 
                placeholder="Example Field" 
                onChange={evt => dispatch.updateData(
                    testIndex, 
                    evt.target.name, 
                    evt.target.value
                )} />
        )}</MainContext.Consumer>
    );
};

So, what's going on here? It appears the state is being set correctly, and even if I wrap the updateData method into a useCallback with the deps set to the state hook, it will still fail to console out in the useEffect.

GROVER.
  • 4,071
  • 2
  • 19
  • 66
  • Return new instance of state:: return [...candidates], if state refererence will not be changed, useeffect will not be triggered by data. – Oleg May 03 '22 at 03:17
  • @Oleg I'm not quite sure what you mean, sorry. – GROVER. May 03 '22 at 03:19
  • 1
    In updateData function, change return canditates to return [...candidates] – Oleg May 03 '22 at 03:21
  • @Oleg That's a bit quirky. I'm surprised you cannot simply return the array as is. Care to share an answer which elaborates a bit? :-) Thank you regardless. – GROVER. May 03 '22 at 03:22
  • 1
    Read here https://stackoverflow.com/questions/26253351/correct-modification-of-state-arrays-in-react-js and https://stackoverflow.com/questions/57538610/react-usestate-array-mutation-error – Oleg May 03 '22 at 03:24
  • 1
    When you update the state react checks for `currentData === dataReturnedFromSetDataCallback`. And in _Javascript_ if you do `const arr1 = [1,2,3]` and `const arr2=arr1` , no matter how you update the `arr1` your `arr2` will be same as the `arr1` since you are using same references. – shubham May 03 '22 at 03:30

0 Answers0