Search code examples
javascriptnext.jsswr

mutate in useSWR hook not updating the DOM


It's a next.js app and I populate the data using a useSWR hook.

const { data, error } = useSWR('/api/digest', fetcher, {
    revalidateOnFocus: false,
})

The issue is that the DOM doesn't get updated as expected after the mutate() line below. But if I hard code the data as updatedData and pass it as the arg for the mutate it works normally. The fact is that data and the updatedData are the same. See comments below.

Edit: If I click on any Navbar <Link/> it gets updated.

Any clues of what is happening?

const handleAddRowClick = async () => {

    const newRow = { category: selectedCategory, entity: selectedEntity }
    data.subscription[selectedCategory].push(selectedEntity);

    console.log(data); // This data is equal to updatedData
    
    const updatedData = {
        "subscription": {
            "materialFact": [
                "Boeing"
            ],
            "headlines": [
                "thejournal",
                "onemagazine" // ***This is the value added.
            ]
        }
    }
    // mutate('/api/digest', data, false) // This does not works.
    mutate('/api/digest', updatedData , false) // It works.
}

Solution

  • I am assuming that the data property you use in handleAddRowClick is the same that you get from useSWR. Now, if you update some deeply nested object in data, it doesn't change the data reference. It still points to the old object. So, if you pass it again to mutate, for mutate, the object is still the same and hence, it won't trigger a re-render.

    I would recommend that you do something like the following to derive updatedData from data and then pass it to the mutate function.

    const handleAddRowClick = async () => {
        const updatedData = {
            ...data,
            subscription: {
                ...data.subscription,
                [selectedCategory]: [
                    ...data.subscription[selectedCategory],
                    selectedEntity,
                ]
            }
        }
    
        mutate('/api/digest', updatedData , false);
    }
    

    On a side note, you can also use something like immer to simplify copying the state.