0

CONTEXT

I have the following React functional component:

export const LocationProvider: React.FC = ({children}) => {
    const [visLocationData, setVisLocationData] = React.useState<VisLocationData>({});

    const addLocationData = React.useCallback((id: string, rect: ClientRect) => {
        setVisLocationData((prevData) => {
            console.log(`[Debugging]: Setting location data for ${id}`); // Debugging
            prevData[id] = rect;
            return prevData;
        });
    }, []);

    console.log(`[Debugging]: Rendering location provider`); // Debugging

    return (
        <VisLocationContext.Provider value={{visLocationData, addLocationData}}>
            {children}
        </VisLocationContext.Provider>
    )
}

I have ~17 child components calling addLocationData, but the children never see the updated visLocationData state.

Additionally, the print statements I see are [Debugging]: Rendering location provider followed by 17 instances of [Debugging]: Setting location data for ${id}.

Inspecting the Components in the React developer tools, I do see the correct state for the LocationProvider, it just never makes its way down to the children in the provider.

Questions

  1. Why don't the child components ever see the updated visLocationData state?
  2. Shouldn't I see pairs of the print statements I've got- each time the state is updated (...setting location data...)the function is re-called/re-rendered (...Rendering location provider)?

Note

addLocationData is called as part of a ref:

export const Resource: React.FC<Resource<any>> = (props) => {
    const {addLocationData} = useVisLocation();
    const ref = React.useRef(null);

    React.useEffect(() => {
        if (!ref || !ref.current || !addLocationData) {
            return;
        }
        addLocationData( props.id, (ref.current! as HTMLDivElement).getBoundingClientRect());
    }, [addLocationData]);

    return(<div className={"resource"} ref={ref}>{props.id}</div>)
}
DRich
  • 972
  • 1
  • 8
  • 25

1 Answers1

0

The change

        setVisLocationData((prevData) => {
            console.log(`[Debugging]: Setting location data for ${id}`); // Debugging
            prevData[id] = rect;
            return prevData;
        });
    }, []);

should be

        setVisLocationData((prevData) => {
            console.log(`[Debugging]: Setting location data for ${id}`); // Debugging
            return {...prevData, [id]: rect};
        });
    }, []);

The reason

My original approach mutates the existing state object and then returns the mutated state. The corrected approach returns a completely new object.

Details: https://reactjs.org/docs/react-component.html#state

Never mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.

DRich
  • 972
  • 1
  • 8
  • 25