简体   繁体   English

在页面刷新或更改时保存通过Redux传递的数据的问题

[英]Problems saving data passed through Redux when page refreshes or changes

I'm trying to save a user's item selection whenever this item selection is added to a cart. 每当此项目选择添加到购物车时,我正在尝试保存用户的项目选择。 I use Redux to pass the item data whenever the user presses add to cart on a particular item. 每当用户按下添加到特定项目的购物车时,我都会使用Redux传递商品数据。 In my Cart component I can view the the item selection data of the last item the was added to cart. 在我的Cart组件中,我可以查看添加到购物车的最后一个项目的项目选择数据。 This user selection item data looks like Object {price: 25, item: "Hoodie", size: "medium"} . 此用户选择项数据类似于Object {price: 25, item: "Hoodie", size: "medium"} I want to be able to store each selection that is added to the cart in my Cart component. 我希望能够存储我的Cart组件中添加到购物车的每个选择。 This is Cart : 这是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've set up itemBucket() as a function to add each item selection to a cart array found in the state . 我将itemBucket()设置为一个函数,将每个项目选择添加到state找到的购物车数组中。 However this does not work and only the last item added to the cart is passed. 但是这不起作用,只传递添加到购物车的最后一项。 This may have to do with changing how my Redux Store is working, but I don't really know how to apply this. 这可能与改变我的Redux Store的工作方式有关,但我真的不知道如何应用它。 This is my Redux Store: 这是我的Redux商店:

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 can I save each item that is passed to Cart even when the page is refreshed or changed? 即使页面刷新或更改,如何保存传递给Cart每个项目?

EDIT 编辑

Here is my reducer component: 这是我的reducer组件:

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 
    };
}

Currently, whats happening in your app is that every-time your page refreshes, the redux store is initialised and uses the default values provided by the reducers. 目前,您的应用中发生的事情是,每次页面刷新时,redux存储都会初始化并使用reducer提供的默认值。

You can over-ride these default values by providing an object as the second argument into createStore . 您可以通过在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 browser's localStorage to store and retrieve data. 此示例使用浏览器的localStorage来存储和检索数据。

The localStorage.js file uses the redux state as the data to store in localStorage. localStorage.js文件使用redux状态作为要存储在localStorage中的数据。

localStorage.js 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)
    }
}

Now you can configure the redux store so when it initialises, the 'state' item in localStorage is retrieved and will over-ride the default reducer values. 现在,您可以配置redux存储,以便在初始化时,检索localStorage中的“state”项,并覆盖默认的reducer值。

The saveState function is what will persist your redux state. saveState函数将保持redux状态。 It achieves this by listening to changes within your redux store using store.subscribe() . 它通过使用store.subscribe()监听redux商店中的更改来实现此store.subscribe() When changes takes place, saveState will be called. 发生更改时,将调用saveState

Install lodash to enable throttling otherwise saveState will be called too many times. 安装lodash以启用限制,否则saveState将被调用太多次。

configureStore.js 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 in the following way. 现在按以下方式创建商店。

index.js index.js

import configureStore from './configureStore'

const store = configureStore()

This implementation demonstrates how to interact directly with localStorage and have taken this idea from Dan. 此实现演示了如何直接与localStorage交互,并从Dan那里获取了这个想法。 You can later optimise this storage and retrieval process. 您可以稍后优化此存储和检索过程。 Currently, anytime a change occurs in store, the whole redux state is written into localStorage. 目前,只要在商店中发生更改,整个redux状态就会写入localStorage。

Once you're nearer to establishing a data structure for your redux store, you can slowly break out the state trees and set them as individual items within localStorage. 一旦您更接近为redux商店建立数据结构,您可以慢慢分解状态树并将它们设置为localStorage中的单个项目。 (One possible solution) (一种可能的解决方案

You then subscribe/listen to specific state trees instead of the whole store, and save them when changes occur. 然后,您订阅/侦听特定的状态树而不是整个存储,并在发生更改时保存它们。

store.getState().some.data.set instead of store.getState() store.getState().some.data.set而不是store.getState()

Also, check out npm, some people have created some cool ways to address this issue. 另外,请查看npm,有些人已经创建了一些很好的方法来解决这个问题。

My recommendation is to use redux-persist to save the cart state into localStorage. 我的建议是使用redux-persist将购物车状态保存到localStorage。 It will be much easier compared to writing your own implementation and has an active community (so if you encounter any issues/bugs, you probably won't be the only one). 与编写自己的实现相比,它会更容易,并且拥有一个活跃的社区(因此,如果您遇到任何问题/错误,您可能不会是唯一的问题/错误)。

Redux Store Redux商店

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 full documentation here: https://github.com/rt2zz/redux-persist 请参阅此处的完整文档: https//github.com/rt2zz/redux-persist

I've set up itemBucket() as a function to add each item selection to a cart array found in the state. 我将itemBucket()设置为一个函数,将每个项目选择添加到状态中找到的购物车数组中。 However this does not work and only the last item added to the cart is 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 old cart if put in itemBucket as setState is not synchronous. countTotal将显示旧购物车,如果放入itemBucket,因为setState不同步。 you can put however put that in componentDidUpdate. 你可以把它放在componentDidUpdate中。

For between refreshes, either store cart on server by using a service call to post the count, or use localStorage/sessionStorage/indexedDb to keep it in client. 对于刷新之间,可以使用服务调用在服务器上存储购物车以发布计数,或使用localStorage / sessionStorage / indexedDb将其保留在客户端中。 And in componentWillMount, get this from above location and hydrate your redux store with that, on the client. 在componentWillMount中,从上面的位置获取此信息,并在客户端上使用它来补充您的redux商店。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM