Problems persisting data passed through Redux on page refresh or change

I am trying to keep the selection of a user's item when that item is added to the cart. I am using Redux to pass item data when user clicks on cart on a specific item. In my component, Cart

I can view the item selection data of the last item that was added to the cart. The data for this user pick looks like Object {price: 25, item: "Hoodie", size: "medium"}

. I want to be able to store every selection added to the cart in my component Cart

. This is Cart

:

import React, { Component } from 'react';
import {addCart} from './Shop'; 
import { connect } from 'react-redux';

export class Cart extends Component {
    constructor(props) {
        super(props);
        this.state = {items: this.props.cart,cart: [],total: 0};
    }

    itemBucket(item) {
        this.state.cart.push(this.state.items);
        this.countTotal();
    }

    countTotal() {
        var total = 0;
        console.log(this.state.cart);
        this.state.cart.forEach(function(item, index){
            total = total + item.price;
            console.log (total);
        })
    }

    componentDidMount () {
        window.scrollTo(0, 0);
        this.itemBucket();
    }

    render() {
        return(
            <div className= "Webcart" id="Webcart">
                <addCart cartItem={this.props.cart} />
            </div>
        );
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        onCartAdd: (cart) => {
            dispatch(addCart(cart));
        },
    }
}

function mapStateToProps(state) {
  return { cart: state.cart };
}

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

      

I configured itemBucket()

as a function to add each item to the basket array found in state

. However, this doesn't work and only the last item added to the cart gets passed. This may be due to a change in how my Redux store works, but I don't know how to apply this. This is my Redux store:

import { createStore, applyMiddleware } from 'redux';
import  reducer  from './reducers';
import thunkMiddleware from 'redux-thunk';
import {createLogger} from 'redux-logger';


const store = createStore(
  reducer,
  applyMiddleware(
    createLogger(),
    thunkMiddleware
  )
);
export default store; 

      

How do I keep every element that is passed to Cart

, even if the page is refreshed or changed?

EDIT

Here is my reducer component:

import {ADD_CART} from './actions';

export default Reducer;

var initialState = {
  cart:{},
  data: [],
  url: "/api/comments",
  pollInterval: 2000
};

function Reducer(state = initialState, action){
    switch(action.type){
        case ADD_CART:
            return {
                ...state,
                cart: action.payload
            }

            default:
                return state 
    };
}

      

+3


source to share


3 answers


My recommendation is to use redux-persist

to store the state of the cart in localStorage. It will be much easier than writing your own implementation and having an active community (so if you run into any issues / bugs you probably won't be the only ones).

Redux Store

import { createStore, applyMiddleware, compose } from 'redux';
import { persistStore, autoRehydrate } from 'redux-persist';
import reducer from './reducers';
import thunkMiddleware from 'redux-thunk';
import { createLogger } from 'redux-logger';

const store = createStore(
  reducer,
  undefined,
  compose(
    applyMiddleware(createLogger(), thunkMiddleware),
    autoRehydrate()
  )
);

persistStore(store, { whitelist: ['cart'] });

export default store; 

      



Reducer

import { ADD_CART } from './actions';
import { REHYDRATE } from 'redux-persist/constants';

export default Reducer;

var initialState = {
  cart:{},
  data: [],
  url: "/api/comments",
  pollInterval: 2000
};

function Reducer(state = initialState, action){
  switch(action.type){
    case REHYDRATE:
      if (action.payload && action.payload.cart) {
        return { ...state, ...action.payload.cart };
      }
      return state;

    case ADD_CART:
      return {
        ...state,
        cart: action.payload
      }

      default:
        return state 
  };
}

      

See the full documentation here: https://github.com/rt2zz/redux-persist

+1


source


Currently, what is happening in your application is that every time the page is refreshed, the redux store is initialized and uses the default values โ€‹โ€‹provided by the reducers.

You can overload these defaults by providing an object as the second argument to createStore

.

const store = createStore(
    reducer, // default values provided by reducers
    {key: "value"}, // overwrites matching key/val pairs, think Object.assign with the first reducer argument
    applyMiddleware(createLogger(), thunkMiddleware)
)

      


This example uses the localStorage browser to store and retrieve data.

The file localStorage.js

uses redux state as data to be stored in localStorage.

localStorage.js

export const loadState = () => {
    try {
        let serializedState = localStorage.getItem('state')

        if (serializedState === null) {
            return undefined
        }
        let storageState = JSON.parse(serializedState)

        return storageState
    } catch (err) {
        return undefined
    }
}

export const saveState = (state) => {
    try {
        const serializedState = JSON.stringify(state)
        // saves state to localStorage
        localStorage.setItem('state', serializedState)
    } catch (err) {
        console.log('error and unable to save state', err)
    }
}

      

You can now configure the reducer store, so when it is initialized, the "state" item in localStorage is retrieved and will exceed the default reducer values.

A function saveState

is what will persist in the reduction state. This is achieved by listening for changes in your redux store with store.subscribe()

. When changes occur, is called saveState

.

Set lodash to enable throttling, otherwise saveState will be called too many times.



configureStore.js

import { createStore, applyMiddleware } from 'redux'
import  reducer  from './reducers';
import thunkMiddleware from 'redux-thunk'
import createLogger from 'redux-logger'
import { loadState, saveState } from './localStorage'
import throttle from 'lodash/throttle'

let middlewares = [createLogger(), thunkMiddleware]

const configureStore = () => {
    const localStorageState = loadState()

    const store = createStore(
        reducer,
        localStorageState,
        applyMiddleware(...middlewares)
    )

    // everytime the state changes, it will be saved to 
    store.subscribe(throttle(() => {
        saveState(store.getState())
    }, 1000))

    return store
}
export default configureStore

      

Now create your store as follows.

index.js

import configureStore from './configureStore'

const store = configureStore()

      

This implementation demonstrates how to interact directly with localStorage and took that idea from Dan. You can optimize this storage and retrieval process later. Currently, anytime a store change occurs, the entire reduction state is written to localStorage.

Once you get close to creating the data structure for your redux store, you can slowly dump the state trees and set them as separate items in localStorage. (One possible solution)

Then you subscribe / listen to specific state trees instead of the whole store and save them when changes occur.

store.getState().some.data.set

instead store.getState()

Also, check npm, some people have created some cool ways to solve this problem.

+2


source


I've set itemBucket () as a function to add each item to the cart array found in the state. However, this doesn't work and only the last item added to the cart gets passed.

use

 constructor(props) {
    super(props);
    this.state = {cart: this.props.cart,total: 0};
}
 itemBucket(item) {
            this.setState({cart : [...this.state.cart, item]});
        }
    componentDidUpdate(){
      this.countTotal();
    }

      

countTotal will show the old cart if put in itemBucket since setState is not synchronous. you can put it in componentDidUpdate.

In between updates, either store the cart on the server using a service call to post the invoice, or use localStorage / sessionStorage / indexedDb to store it on the client. And in componentWillMount, get this from the upstream location and wet your redux store with this, on the client.

+1


source







All Articles