Redux state resets itself on route change (I think)

Viewed 7079

I am successfully updating my 'global state' with Redux. I change the language index of my app using an action and it changes in the component; however, when I redirect to another component the state seems to reset to the default state value. Help pls...

"react": "^16.13.1", "react-dom": "^16.13.1", "react-redux": "^7.2.0", "react-router": "^5.1.2", "react-router-dom": "^5.1.2", "react-scripts": "3.4.1", "redux": "^4.0.5", "redux-thunk": "^2.3.0"

/redux/state.js

export default {
  language: 0,
  sirens: []
}

/redux/store.js

import { createStore, applyMiddleware } from 'redux'
import reducers from './reducers'
import state from './state'
import thunk from 'redux-thunk';

export default createStore(reducers, state, applyMiddleware(thunk))

/redux/actions.js

export const chLang = (index) => {
  return {
    type: 'CH_LANG',
    value: index
  }
}
export const fetchSirens = () => {
  return  {
    type: 'FETCH_SIRENS',
    value: null
  }
}

export const addSiren = (siren) => {
  return {
    type: 'ADD_SIREN',
    value: siren
  }
}

export const rmSiren = (id) => {
  return {
    type: 'RM_SIREN',
    value: id
  }
}

/redux/reducers.js

import { combineReducers } from "redux";

// language should only store an index
const language = (state = null, action) => {
  switch (action.type) {
    case 'CH_LANG':
      return action.value
    default:
      return state;
  }
}
const sirens  = (state = [], action) => {
  switch (action.type) {
    case 'FETCH_SIRENS':
      return action.value
    case 'ADD_SIREN':
      return [...state, action.value]
    case 'RM_SIREN':
      const sirens = [...state];
      sirens.splice(action.value, 1);
      return sirens;
    default:
      return state;
  }
}


export default(combineReducers({language, sirens}));

Initially, I thought I might be accidentally mutating the initial state in my reducers but I don't think that's the case.

/containers/Landing.js All containers follow this general layout:

import { connect } from "react-redux";
import { chLang } from "../redux/actions";
import Landing from '../components/Landing';

const mapStateToProps = state => {
  return {
    language: state.language
  }
}

const mapDispatchToProps = dispatch => {
  return {
    chLang: index => dispatch(chLang(index))
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(Landing)

/components/Landing.js

import React from 'react';
import languages from '../languages';
import Button from '@material-ui/core/Button';
import SimpleDialog from '../containers/SimpleDialog'
import blob from '../img/blob-white.png';
import siren1 from '../img/siren-1.png';


const Landing = (props) => {
  const [open, setOpen] = React.useState(false);
  const [selectedValue, setSelectedValue] = React.useState(null);
  const didMountRef = React.useRef(false)


  const handleClickOpen = () => {
    setOpen(true);
  };

  const handleClose = (value) => {
    props.chLang(value)
    setOpen(false);
    setSelectedValue(value);
  };
  React.useEffect(() => {
    if (didMountRef.current) {
      // debugger
      if(props.language !== null){
        // window.location.replace("/hi")
      }
    } else {didMountRef.current = true}
  })

  return (
    <div className="landing">
      <h2 className="sentence">
        <div className="slidingVertical">
          {languages.types.map((lan, i) => <span key={i+1}>{lan.landing[0]}</span>)}
        </div>
      </h2>
      <h1>Siren.</h1>
      <p>You speak {languages.types[props.language].language}</p>
      <div className="landing-img">
        <img id="blob" src={blob} />
        <img id="siren" src={siren1} />
      </div>
      <Button className="landing-bttn" variant="contained" color="primary" onClick={handleClickOpen}>
      <h2 className="sentence sentence-bttn">
        <div className="slidingVertical">
          {languages.types.map((lan, i) => <span className="lang-bttn" key={i+1}>{lan.landing[1]}</span>)}
        </div>
      </h2>
      </Button>
      <SimpleDialog languages={languages} selectedValue={selectedValue} open={open} onClose={handleClose} />
    </div>
  );
}

export default Landing;

The issue comes up when I comment out that window.location.replace("/hi") and the app then loads up the "hi" component. That component should display the text with the language index that the user selected from the previous component; however, the language resets itself to the default value in the store (0).

/components/Hi.js

import React from 'react';
import languages from '../languages';
import Button from '@material-ui/core/Button';
import '../css/hi.css';
import blob from '../img/blob-2.png';
import siren from '../img/siren-1.png'

class Hi extends React.Component {
  state = {
    text: languages.types[this.props.language].hi
  }

  checkSpecialLang = () => {
    switch(this.props.language){
      case 3:
        return'zh'
      case 4:
        return 'hi'
      default:
        console.log('Muffin works! I\'m in quarantine and I\'mma cry ;(')
    }
  }

  handleAgree = () => {
    //     window.location.replace("/track");
  }

  render() {
    // debugger;
    console.log(this.props.language)
    return (
      <div id={this.checkSpecialLang()} className="hi-container">
       <h1>{this.state.text[0]}</h1>
       <img className='siren-img' src={siren} />
       <div className='intro'>
         <p>{this.state.text[1]}</p>
         <img src={blob} />
       </div>
       <p className="question">{this.state.text[2]}</p>
       <Button color='secondary' className="agree-cookies-bttn" variant="contained" onClick={this.handleAgree}>
         <p>{this.state.text[4]}</p>
       </Button>
     </div>
    );
  }
}

export default Hi;

What am I missing here?!

3 Answers

That's because location.replace("/hi") reloads whole window and redux store also resets. To redirect user and keep redux state unchanged, you should route between components. I mean you must change route without reloading the whole page. I suggest you using react routing module.

You can use react-router-dom's Redirect component to redirect user without reloading page.

Check out the documentations here

For any one else who has this problem. Use a <Link> tag as opposed to an <a> tag. The a tag will reload the page entirely and remove the current state. A Link tag allows you to retain your data through out the application but still allows you to move between pages in your app.

window.location refreshes the whole redux state. To keep the state maintained either use the "Link" component or "Redirect" component to route through React components.

import  { Redirect } from 'react-router-dom'
 <Redirect to='/hi'  />
Related