facebook / react

The library for web and native user interfaces.
https://react.dev
MIT License
229.24k stars 46.95k forks source link

Introducing `UseConstantState()` - A Novel Approach to Permanent State Management šŸš€ #28357

Closed Mahdi-Hazrati closed 9 months ago

Mahdi-Hazrati commented 9 months ago

šŸ‘‹ Hi there, esteemed Facebook/React maintainers,

I hope this message finds you well. My name is Mahdi Hazrati, and I'm a software engineer at nextproduction.dev. I'm reaching out to share an innovative concept for enhancing state management within the React library.

I'd like to introduce the notion of useConstantState() - a novel approach to state management that tackles the persistent challenge of data loss upon page reloads or route changes. This concept aims to provide developers with a seamless experience in managing state within React applications.

At its core, useConstantState() functions akin to useState(), but with an added layer of durability. It ensures that state remains accessible even amidst browser refreshes or navigation transitions by leveraging IndexedDB for data storage.

Allow me to shed some light on the workings of this concept. Upon initialization, useConstantState() generates a unique key and utilizes IndexedDB to store the state object associated with this key. Subsequently, upon each component render, it retrieves and updates the state from this persistent storage, thus preserving its integrity across sessions.

The genesis of this idea stems from real-world scenarios encountered within my development endeavors at nextproduction.dev. Often, our team found the need to retain certain application states across page reloads, prompting me to explore innovative solutions within the React ecosystem.

To illustrate the practical application of this concept, I've crafted an initial implementation as follows:

import React, { useState, useEffect } from "react";

/**
 * Custom React hook for managing state persistence using IndexedDB.
 * @param {*} initialValue - The initial value of the state.
 * @returns {[*, function]} - A tuple containing the state value and a function to update it.
 */
function useConstantState(initialValue) {
    // Define state variable and setter
    const [state, setState] = useState(initialValue);
    // Generate a unique key for this state
    const key = "state_" + Math.random().toString(36).substr(2, 9);

    // Effect to handle state persistence
    useEffect(() => {
        // Open IndexedDB database
        const openRequest = indexedDB.open("ReactIndexedDB", 1);

        // Handle database upgrade
        openRequest.onupgradeneeded = function (event) {
            const db = event.target.result;
            // Create object store
            db.createObjectStore("state", { keyPath: "key" });
        };

        // Handle database opening success
        openRequest.onsuccess = function (event) {
            const db = event.target.result;
            const transaction = db.transaction(["state"], "readwrite");
            const objectStore = transaction.objectStore("state");
            // Get state value from IndexedDB
            const getRequest = objectStore.get(key);

            getRequest.onsuccess = function (event) {
                // Set state to retrieved value if available, otherwise use initial value
                if (getRequest.result) {
                    setState(getRequest.result.value);
                } else {
                    setState(initialValue);
                }
            };

            // Close database connection
            transaction.oncomplete = function () {
                db.close();
            };
        };

        // Handle database opening error
        openRequest.onerror = function (event) {
            console.error("Error opening IndexedDB", event);
        };
    }, [initialValue, key]);

    // Function to update state and persist to IndexedDB
    const setConstantState = (newValue) => {
        const openRequest = indexedDB.open("ReactIndexedDB", 1);

        // Handle database opening success
        openRequest.onsuccess = function (event) {
            const db = event.target.result;
            const transaction = db.transaction(["state"], "readwrite");
            const objectStore = transaction.objectStore("state");

            // Update state value in IndexedDB
            objectStore.put({ key, value: newValue });

            // Close database connection
            transaction.oncomplete = function () {
                db.close();
            };
        };

        // Handle database opening error
        openRequest.onerror = function (event) {
            console.error("Error opening IndexedDB", event);
        };

        // Update state locally
        setState(newValue);
    };

    // Return state and setter
    return [state, setConstantState];
}

export default useConstantState;

I firmly believe that integrating this concept into React could significantly enhance developer productivity and user experience. Should the React maintainers express interest in exploring this idea further, I am more than willing to collaborate in refining and incorporating it into the React core.

Thank you for considering this proposal, and I eagerly await your feedback.

Warm regards, Mahdi Hazrati Software Engineer mahdi@nextproduction.dev

blghns commented 9 months ago

Hi Mahdi! Nice concept and a good suggestion. I'd like to give you my feedback.

  1. This feels like it should be a separate package and not a core React functionality.
  2. There are solutions out there that implement a similar solution, like https://github.com/donavon/use-persisted-state this package
  3. Randomizing the key is bad and could cause a collision. In this instance, it is easily avoidable. I'd probably change it so that the key is an argument instead of a randomized string.
  4. Using indexdb and not local storage is an interesting choice. Wonder how it performs against local storage.
  5. I think it might be more interesting if this hook exposed indexedDb in some way... Like rename this to usePersistedDatabase hook and return the . Then you could use the querying abilities of the database itself for more complex stuff. You could even combine it with https://jsstore.net/docs/get-started and query things with that syntax
Connection.select({
    From: "YourTableName"
    Where: {
        Column1: 'value1',
        Column2: 'value2'
    },
    OnSuccess:function (results){
        console.log(results);
    },
    OnError:function (error) {
        console.log(error);
    }
});

Warn regards, Bilgehan

markerikson commented 9 months ago

This is pretty clearly a GPT-generated submission, not "novel" (lots of prior art), and not something that ought to be included in the React core. Not only that, but React works in environments where IndexedDB doesn't even exist.

rickhanlonii commented 9 months ago

Agree with @markerikson

Mahdi-Hazrati commented 9 months ago

This is pretty clearly a GPT-generated submission, not "novel" (lots of prior art), and not something that ought to be included in the React core. Not only that, but React works in environments where IndexedDB doesn't even exist.

Hi My native language is not English, and in order to make the request look professional, I asked Chatgpt for help in writing the request text, and this is not a bad thing at all.

rickhanlonii commented 8 months ago

@Mahdi-Hazrati yeah totally understandable. One thing that might be helpful would be to mention that when you submit an issue just as a heads up. As maintainers, we are starting to get a ton of chatgpt/ai generated issues and it's hard to know which issues are generated vs assisted by AI.

For the proposal itself, a custom hook like you wrote is the perfect use case for custom hooks. It doesn't make sense to add something like this to React itself when a) it's possible to write it in user land, and b) not every environment can use IndexedDB as @markerikson mentioned.

For something like this to work in a generic way, React would want to have a hook for storing state, with an initial value from storage like IndexedDB, and a way to sync the updates back to IndexedDB. Then, users could build their own hooks specific to where the state is saved and how it's sync'd - but React already has those hooks: useState and useEffect.

Mahdi-Hazrati commented 8 months ago

Hello @rickhanlonii ,Thanks for the detailed explanation About this issue. This was an idea and a suggestion about a problem in state management in react application programming. State value is in page reloaded or route changes, lost In my programming workflow I some times want my state value keep in stay, As react maitain team Please find a solution for this problem suggest from @blghns to convert this hook to separate npm package is good idea, I refactor my code and convert to usable hook in [Browser] application;)