inertiajs / inertia

Inertia.js lets you quickly build modern single-page React, Vue and Svelte apps using classic server-side routing and controllers.
https://inertiajs.com
MIT License
6.3k stars 423 forks source link

Multiple useRemember() not working as expected #1950

Closed bubokko closed 3 weeks ago

bubokko commented 1 month ago

Version:

Describe the problem:

Multiple useRemember() calls in the same component produce unreliable results. The value being remembered is only the last one. I believe it's by design, but it would be much more convenient to use it like useState(), where we'd keep each value separate instead of packing them all together.

Steps to reproduce:

Try this page:

import React from 'react';
import { useRemember } from '@inertiajs/react';

export default function () {
    const [foo, setFoo] = useRemember(1);
    const [bar, setBar] = useRemember(2);

    return (
        <>
            {foo} {bar}
        </>
    );
};

When visiting the page, it correctly displays 1 2. But going back and forward in history, it displays 2 2. Also, the first refresh gives 2 2 and following refreshes display 1 2 again.

pedroborges commented 3 weeks ago

The issue you're seeing is actually expected behavior due to the way useRemember() works. In your example, you're missing the unique key as the second argument for each useRemember() call. Without this, Inertia defaults to using a shared key (default), which causes one of the states to override the other.

To fix this, simply provide a unique key for each call, and that will ensure each state is remembered independently, much like how you'd expect with useState(). Here's how you could adjust your code:

const [foo, setFoo] = useRemember(1, 'foo');
const [bar, setBar] = useRemember(2, 'bar');
bubokko commented 3 weeks ago

@pedroborges that's interesting and helpful, thank you! Wouldn't it make more sense if the default key was sequential? So each consecutive useRemember() call would increase the default key?

I checked again in the docs and the docs suggest using only one useRemember() per component and the second parameter is assumed to be helpful for scenarios when different components use useRemember() at the same time.

Isn't there any way to make it work like useState() though? It's the same kind of problem and React doesn't require providing unique keys for useState(). Instead, it relies on the fact that hooks need to be called unconditionally and always keep their sequence right. Why can't useRemember() do the same thing?

pedroborges commented 3 weeks ago

@bubokko Glad it helped! useRemember() uses the Inertia's core router.remember() method under-the-hood and is designed to work with multiple front-end adapters like Vue.js and Svelte. It persists state in the browser history, which is why unique keys are needed. Unlike useState(), its focus is on maintaining state across history navigation rather than local component state, so matching useState behavior isn't the goal.

bubokko commented 3 weeks ago

@pedroborges I understand it and I've also thought about that. Still, I believe it could be possible to store these values in array-like manner, sequentially. Perhaps it would be quite complex to implement logic of assigning the values to components, but having them sequential per-component sounds doable. But yeah, maybe the fact that React is not the only target platform is the problem.