Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Redux state is lost at page refresh

in my react app I have 3 components. from the 1st component by a button, I use Link to go to the 2nd component. at the 2nd I create a state (redux store), manipulate it and when manipulation is finished by submitting button I redirect to the 3rd component. at the 3rd component, I also see the state (by redux chrome tool) but here when I refresh the page (the webpack thing when a code is changed and saved), I lose the sate and get an empty Object.

this is the structure of my app.

index.js

const store = createStore(
  rootReducer,
  composeWithDevTools(applyMiddleware(thunk))
);

ReactDOM.render(
  <BrowserRouter>
    <Provider store={store}>
      <Route component={App} />
    </Provider>
  </BrowserRouter>,
  document.getElementById("root")
);

App.js

const App = ({ location }) => (
  <Container>
    <Route location={location} path="/" exact component={HomePage} />
    <Route location={location} path="/GameInit" exact component={GameInit} />
    <Route location={location} path="/Battle" exact component={Battle} />
  </Container>
);

App.propTypes = {
  location: PropTypes.shape({
    pathname: PropTypes.string.isRequired
  }).isRequired
};

export default App;

Here at HomePage, I have a button that links to GameInit

const HomePage = () => (<Button color="blue" as={Link} to="/GameInit">START GAME</Button>);
export default HomePage;

and the GameInit Page looks like

class GameInit extends Component {
  componentWillMount() {
    const { createNewPlayer} = this.props;
    createNewPlayer();
  }
/* state manipulation till it gets valid */
 palyerIsReady()=>{ history.push("/Battle");}
 
export default connect(mapStateToProps,{ createNewPlayer})(GameInit);

and finally Battle component where I first see the state but lose on refresh

class Battle extends Component {
  render() {return (/*some stuff*/);}}

Battle.propTypes = {
  players: PropTypes.object.isRequired // eslint-disable-line
};
const mapStateToProps = state => ({
  players: state.players
});
export default connect(mapStateToProps,null)(Battle);
like image 357
Amir-Mousavi Avatar asked Sep 04 '18 07:09

Amir-Mousavi


3 Answers

You can easily persist it in the localstorage. Check the example below.

const loadState = () => {
  try {
    const serializedState = localStorage.getItem('state');
    if(serializedState === null) {
      return undefined;
    }
    return JSON.parse(serializedState);
  } catch (e) {
    return undefined;
  }
};

const saveState = (state) => {
  try {
    const serializedState = JSON.stringify(state);
    localStorage.setItem('state', serializedState);
  } catch (e) {
    // Ignore write errors;
  }
};

const peristedState = loadState();

store.subscribe(() => {
  saveState(store.getState());
});

const store = createStore(
  persistedState,
  // Others reducers...
);

render(
  <Provider store={store}>
    <App/>
  </Provider>,
  document.getElementById('root');
);

Serialization is an expensive operation. You should use a throttle function (like the one implemented by lodash) to limit the number of saves.

Eg:

import throttle from 'lodash/throttle';

store.subscribe(throttle(() => {
  saveState(store.getState());
}, 1000));
like image 60
Alexandre Annic Avatar answered Oct 17 '22 08:10

Alexandre Annic


You can use something like redux-persist to save the redux state to local storage or to another storage system.

like image 43
illiteratewriter Avatar answered Oct 17 '22 06:10

illiteratewriter


My personal advice for hot reloading is using this: React hot loader, this prevents page reload and only switch out the altered chunk. This implies that your state never gets lost while devving.

It is pretty easy to install. You only have to add an entry and wrap your initial component in hot which you can get from the package.

If you need more information on how it works i suggest watching this talk by the creator.

like image 1
jovi De Croock Avatar answered Oct 17 '22 07:10

jovi De Croock