Adam
Adam

Reputation: 1304

React w/ Redux/React-Redux and Redux-Persist only persisting first change to local storage

I'm having a few issues with persisting Redux in React using React-Redux and Redux-Persist.

Reducer Example:

export default (state = {}, action) => {
    switch (action.type) {
      case "SET_FOO":
        return action.payload
      default:
        return state;
    }
  };

Action Example:

const setFooAction = (payload) => {
    return {
      type: "SET_FOO",
      payload
    }
  }
  export default setFooAction;

Index.js

import { createStore, combineReducers } from "redux";
import { persistStore, persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";
import { PersistGate } from 'redux-persist/integration/react'
import fooReducer from "redux/reducers/setFooReducer";

const persistConfig = {
  key: "root",
  storage
};

const rootReducer = combineReducers({
  foo: fooReducer 
});

const persistedReducer = persistReducer(persistConfig, rootReducer);

let state = {
  foo: {}
}

let store = createStore(
    persistedReducer, 
    state, 
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
let persistor = persistStore(store);

ReactDOM.render(
  <Provider store={store}>
    <PersistGate loading={null} persistor={persistor}>
      <App />
    </PersistGate>
  </Provider>,
  document.getElementById("root")
);

Usage in component:

import { connect } from "react-redux";
import setFoo from "redux/actions/setFooAction";

class Main extends Component {        
    //... More Code
    componentDidMount() {
        let foo = this.getFoo() //Gets foo, doesn't really exist but just for context
        this.props.setFoo(foo); //Works fine, all gets set, data is persisted
    }    
    addNewFoo = () => {    
        //Called after functions runs somewhere    
        let newFoo = this.getNewFoo() //Gets newFoo, doesn't really exist but just for context
        foo.push(newFoo)
        this.props.setFoo(foo); //Sets Redux store, on refresh, doesn't persist    
    }
    //...More Code    
}

const mapStateToProps = (state) => ({
  ...state,
});

const mapDispatchToProps = (dispatch) => ({
   setFoo: (payload) => dispatch(setFoo(payload)) 
});

export default connect(mapStateToProps, mapDispatchToProps)(Main);

Initially, everything works fine:

However...

If I decide to add anything new into my objects and set/update them into the Redux store...

... on refresh, only what was originally persisted is still present during REHYDRATE (if I am understanding the Redux events right).

Now I believe this is something to do with my objects immutability and Redux not picking up changes because of that, but I have attempted to alter my reducers without any luck:

Reducer Attempt:

export default (state = {}, action) => Object.assign(state, {
   foo: action.payload !== "SET_FOO" ? state.foo : action.payload
});

I tried the above (and also a combintation of this and my original which just broke things). This sort of worked, but was setting my state as follows:

state {
    foo: {
        foo: {
           //Foo data
        }
    }
}

I know that it's just assigning to foo again because of the addition foo: in the reducer...

...however, I am unsure how to correct this without potentially doing something wrong in Redux!

So, my caution has prevailed and I thought best to ask if anyone has any suggestions or threads that I can pull at!

Thanks for any help in advance!!!

Upvotes: 0

Views: 1411

Answers (1)

lndgalante
lndgalante

Reputation: 420

Everything seems good at first glance.

Your Object.assign in the reducer may be what is causing the nested foo objects, because of one on Redux principles that is immutability so you should always return a new state, instead, you're mutating directly the state.

You can add one first parameter more, to create a new object that will have the state properties with the new foo one:

export default (state = {}, action) => Object.assign({}, state, {
   foo: action.payload !== "SET_FOO" ? state.foo : action.payload
});

A better approach and that you will see often will be:

export default (state = {}, { type, payload }) => {
  switch (type) {
    case 'SET_FOO': {
      return { ...state, payload }
    }

    default: {
      return state
    }
  }
}

In this last example, you can clearly visualize that I'm returning a new object with all the properties that the state has, plus the new payload.

Good luck!

Upvotes: 2

Related Questions