Loosing component data on page refresh in react
Asked Answered
M

3

7

I am working on something where I need to maintain the application level state i.e global state, I am using react hooks for that useContext and useReducer.

So what I am doing is on button click I am setting up my context and then using it thought my application by registering the provider in my App.js.

I know why I am using my data, because I am setting the initial state to null first, that's why when I refresh the page it sets to null again, but my requirement is not that after button click I want to store that data as a global state so that I can use it further

But this should not be the case I want to make my data global and on refresh it should not lose

My code

My context file

import React, { useReducer, createContext } from "react";

const initialstate = {
  someData: null
};

const MyContext = createContext({
  someData: null,
  click_btn: d => {}
});
const MyReducer = (state, action) => {
  switch (action.type) {
    case "BTNCLICKED":
      return {
        ...state,
        someData: action.payload
      };

    default:
      return state;
  }
};

const MyProvider = props => {
  const [state, dispatch] = useReducer(MyReducer, initialstate);
  const click_btn = d => {
    dispatch({
      type: "BTNCLICKED",
      payload: d
    });
  };

  return (
    <MyContext.Provider
      value={{ someData: state.someData, click_btn }}
      {...props}
    />
  );
};

export { MyContext, MyProvider };

My home code where I am setting the context

    import React, { useContext, useState } from "react";
import history from "../History/history";

import { MyContext } from "../context/testContext";

function Test() {
  const context = useContext(MyContext);
  const [data, setdata] = useState(null);
  const clickEvt = () => {
    setdata("Setting data");
    context.click_btn(data);
  };
  return (
    <div>
      <input type="button" onClick={clickEvt} value="Click Me" />
    </div>
  );
}

export default Test;

And my app.js file

    import React from "react";
import "./styles.css";
import { MyProvider } from "./context/testContext";
import { Router } from "react-router-dom";
import history from "./History/history";
import Routes from "./myRoutes";

export default () => {
  return (
    <MyProvider>
      <Router history={history}>
        <div className="App wrapper">
          <Routes />
        </div>
      </Router>
    </MyProvider>
  );
};

Please do check working code My codesand box link

Please do check the react developer tools

On click my developer tool image

When I refresh the page developer tool

Murphy answered 20/6, 2020 at 14:33 Comment(0)
R
3

To persist a React context, or a redux store or anything that lives in your app memory, you need to store it outside and then reload it when the app starts (when you press F5, the page is refreshed, your React app is restarted so it loses all components states, contexts, stores...).

You could save your context in local storage for instance, and modify your context so it starts by searching for a stored value and reload it first. It could be done using useEffect in your context : each time it changes, store it in local storage, and preload local storage value on startup:

// context
[...]

useEffect(() => {
  if(localStorage.getItem('myKey')) {
    setState(localStorage.getItem('myKey');
  }
}, []);

useEffect(() => {
  localStorage.setItem('myKey', state);
}, [state]);

This is roughly how it can be done.

You could also choose to persist your context on a remote server, or a local noSQL DB. You will find plenty of solutions:

Richma answered 20/6, 2020 at 15:0 Comment(3)
If I can store in local storage then wy need to create a global state I can easily store the data in local storage and then I can fetch from thereMurphy
Context API gives you a way to easily retrieve data from the global state from all your components, and maybe add some logic. But yes, both allows you to store global state, and one survive on page refresh.Richma
Quoting from your other comment : "Maintain state in React only" that would survive a page refresh, no local storage, no external DB, this is a tough one :). This is react-redux persisting package but the problem and solutions are similar, so you could find inspiration: github.com/rt2zz/redux-persist#storage-enginesRichma
M
1

From your comment

local storage is not a good option I don't want to put this values there as anybody can delete them, and I want to maintain the state in react only

In this case, LocalStorage is client side, react is a framework which is also client side. Anything in client side can be edited in any way. You can't rely data in client side, with assumption of malicious intend. If there's something React can get or edit, it can be edited by anyone who has JS knowledge.

The only thing you can trust is, your backend server. Client sends what user wants to do, and server valid it then handle the request, sends back the result to client.

You can search this keyword for more detail.

client server prevent cheat

Manned answered 20/6, 2020 at 15:18 Comment(3)
Sensitive information that must be trusted must go through the backend server, but not all values need to be trusted value. Any value that comes to the client, including tokens, can be manipulated anyway. Your answer also makes sense. However, even if the answer that the questioner really want seems that does not be lost even if he or she refresh in client side.Pearman
Therefore, i think it's not necessary to store all of the values to server that do not affect the service even if the values are manipulated.Pearman
Assume OP understand sensitive and non-information, and still refuse to put any data in localStorage, then react state is not an option for him as well. Security level of localStorage or react state, they are almost the same.If one is able to manipulate localStorage, react state is also easy to be manipulated, which OP wants to prevent.Manned
A
0

There is missing persistence mechanism. You can extend MyProvider function so it gets value from localStorage or api

Example for API


const MyProvider = props => {
  const [state, dispatch] = useReducer(MyReducer, initialstate);
  const click_btn = payload => {
    saveStateToApi(payload)
      .then(result => {
         dispatch({
           type: "BTNCLICKED",
           payload
         });
      })
  };

  useEffect(() => {
    getLastStateFromApi()
      .then(data => {
        dispatch({
          type: "BTNCLICKED",
          payload: data
        });
      })

  }, [])

  return (
    <MyContext.Provider
      value={{ someData: state.someData, click_btn }}
      {...props}
    />
  );
};
Amathiste answered 20/6, 2020 at 14:55 Comment(3)
Hey local storage is not a good option I don't want to put this values there as anybody can delete them, and I want to maintain the state in react only, like for my login thing the user data was loosing when i refresh but for that i have written an api which gives me current api,but for this one I cannot write any query to backendMurphy
It's not like you have many options. With page refresh you are losing the whole react state as the html has to be fetched and react bootstrapped again and you can either use (if supported) browser persistence mechanism like localStorage, sessionStorage, indexedDb or external api.Planarian
Inspiration from redux-persist : github.com/rt2zz/redux-persist#storage-enginesRichma

© 2022 - 2024 — McMap. All rights reserved.