Search code examples
reactjsreact-hooksreact-state-management

React Hook not notifying state change to components using the hook


So I have a Hook

export default function useCustomHook() {
    const initFrom = localStorage.getItem("startDate") === null? moment().subtract(14, "d"): moment(localStorage.getItem("startDate"));
    const initTo = localStorage.getItem("endDate") === null? moment().subtract(1, "d"): moment(localStorage.getItem("endDate"));
    
    const [dates, updateDates] = React.useState({
        from: initFrom,
        to: initTo
    });

    const [sessionBreakdown, updateSessionBreakdown] = React.useState(null);

    React.useEffect(() => {
        api.GET(`/analytics/session-breakdown/${api.getWebsiteGUID()}/${dates.from.format("YYYY-MM-DD")}:${dates.to.format("YYYY-MM-DD")}/0/all/1`).then(res => {
            updateSessionBreakdown(res.item);
            console.log("Updated session breakdown", res);
        })
    },[dates])

    const setDateRange = React.useCallback((startDate, endDate) => {
        const e = moment(endDate);
        const s = moment(startDate);
        localStorage.setItem("endDate", e._d);
        localStorage.setItem("startDate", s._d);
        updateDates((prevState) => ({ ...prevState, to:e, from:s}));
    }, [])

    const getDateRange = () => {
        return [dates.from, dates.to];
    }
    
    return [sessionBreakdown, getDateRange, setDateRange]

}

Now, this hook appears to be working in the network inspector, if I call the setDateRanger function I can see it makes the call to our API Service, and get the results back.

However, we have several components that are using the sessionBreakdown return result and are not updating when the updateSessionBreakdown is being used.

i can also see the promise from the API call is being fired in the console. Updated session breakdown {status: 'session breakdown data - website: 3d5e9fa4-ac6a-42f9-8f5f-c49168ebe667 (day-by-day)', item: {…}}

I have created a small version that reproduces the issue I'm having with it at https://codesandbox.io/s/prod-microservice-kq9cck Please note i have changed the code in here so it's not reliant on my API Connector to show the problem,


Solution

  • To update object for useState, recommended way is to use callback and spread operator.

    updateDates((prevState) => ({ ...prevState, to:e, from:s}));
    

    Additionally, please use useCallback if you want to use setDateRange function in any other components.

    const setDateRange = useCallback((startDate, endDate) => {
        const e = moment(endDate);
        const s = moment(startDate);
        localStorage.setItem("endDate", e._d);
        localStorage.setItem("startDate", s._d);
        updateDates((prevState) => ({ ...prevState, to:e, from:s}));
    }, [])
    

    Found the problem: You are calling CustomHook in 2 components separately, it means your local state instance created separately for those components. So Even though you update state in one component, it does not effect to another component.

    To solve problem, call your hook in parent component and pass the states to Display components as props.

    Here is the codesandbox. You need to use this way to update in one child components and use in another one.

    If wont's props drilling, use Global state solution.