Redux 和 immutable js,我在尝试使用 immutable 后失去了状态

Redux and immutable js, I lost my state after trying to use immutable

在开始使用 reactjs 和 redux 进行开发后,我认为在使用 redux 的同时使用 immutable.js 会更好。

但是...可能是我智障还是需要一些练习才能正确使用它,一切都崩溃了。

如果您能帮助理解这里出了什么问题,那就太棒了!

所以,这是我的第一个代码:

export function posts(state = {
  isFetching: true,
  didInvalidate: false,
  items: []
}, action) {
  switch (action.type) {
    case INVALIDATE_REQ:
        return Object.assign({}, state, {
            didInvalidate: true
        });
    case REQUEST_POSTS:
        return Object.assign({}, state, {
            isFetching: true,
            didInvalidate: false
        });
    case RECEIVE_POSTS:
        return Object.assign({}, state, {
            isFetching: false,
            didInvalidate: false,
            items: action.posts
        });
    default:
      return state;
  };
};

我是这样改造的:

const initPostState = Map({
  isFetching: true,
  didInvalidate: false,
  items: []
});
export function posts(state = initPostState, action) {
  switch (action.type) {
    case INVALIDATE_REQ:
      return state.set('didInvalidate', true);
    case REQUEST_POSTS:
      return state.set({
        isFetching: true,
        didInvalidate: false
      });
    case RECEIVE_POSTS:
      return state.set({
        isFetching: false,
        didInvalidate: false,
        items: action.posts
      });
    default:
      return state;
  };
};

我的容器 MapStateToProps:

function mapStateToProps(state) {
  const {
    posts: isFetching,
    posts
  } = state.posts;

  console.log(state);
 ...

所以问题是,我如何访问我的状态? 状态控制台报告:

我迷路了!求助!

如果您将 ImmutableJS 与 redux 一起使用,那么您的整个应用程序状态都是不可变的。在connect函数中,使用state.get("posts")访问posts状态。然后您将不得不使用 get() 访问 posts 状态属性。或者您可以使用 toJs() 来避免必须在组件内部操作不可变。

您需要使用 immutableJS 中的 get 方法

使用 state.get('didInvalidate') 访问 didInvalidate 的值,对于其他值也是如此。

如果您使用的是 javascript 对象,那么您可以像 state.get('something').toJS()

这样做应该会给你灵感

function mapStateToProps(state){
  const isFetching = state.get('isFetching'),
  const items = state.get('items').toJS();
}

切勿在 mapStateToProps 中使用 toJS()。来自 Redux 文档:

Converting an Immutable.JS object to a JavaScript object using toJS() will return a new object every time. If you do this in mapSateToProps, you will cause the component to believe that the object has changed every time the state tree changes, and so trigger an unnecessary re-render.

如果您的应用需要高性能,您必须在 Dumb 组件中使用 Immutable.js 及其 get()getIn() 助手。

Also since ImmutableJS has versatile API, in most cases it removes the need for helper libraries like lodash.

但在大多数情况下,您可以使用他们建议的代码通过牺牲性能将 Immutable.js 与您的组件分开。

一个 HOC 组件(最新的 immutable.js 使用 isIterable 谓词):

import React from 'react';
import { Iterable } from 'immutable';

export const toJS = (WrappedComponent) =>
   (wrappedComponentProps) => {
       const KEY = 0;
       const VALUE = 1;

       const propsJS = Object.entries(wrappedComponentProps)
            .reduce((newProps, wrappedComponentProp)  => {
                newProps[wrappedComponentProp[KEY]] = 
                    Iterable.isIterable(wrappedComponentProp[VALUE]) 
                        ? wrappedComponentProp[VALUE].toJS() 
                        : wrappedComponentProp[VALUE];
                return newProps;
            }, {});

       return <WrappedComponent {...propsJS} />
   };

一个 HOC 组件(最新的 immutable.js 使用 isImmutable 谓词):

import React from 'react';
import { isImmutable } from 'immutable';

export const toJS = (WrappedComponent) =>
   (wrappedComponentProps) => {
       const KEY = 0;
       const VALUE = 1;

       const propsJS = Object.entries(wrappedComponentProps)
            .reduce((newProps, wrappedComponentProp)  => {
                newProps[wrappedComponentProp[KEY]] = 
                    isImmutable(wrappedComponentProp[VALUE]) 
                        ? wrappedComponentProp[VALUE].toJS() 
                        : wrappedComponentProp[VALUE];
                return newProps;
            }, {});

       return <WrappedComponent {...propsJS} />
   };

使用方法:

import { connect } from 'react-redux';

import { toJS } from './to-js';

import DumbComponent from './dumb.component';

const mapStateToProps = (state) => {
   return {
       /**
       obj is an Immutable object in Smart Component, but it’s converted to a plain 
       JavaScript object by toJS, and so passed to DumbComponent as a pure JavaScript 
       object. Because it’s still an Immutable.JS object here in mapStateToProps, though, 
       there is no issue with errant re-renderings.
       */
       obj: getImmutableObjectFromStateTree(state)
   }
};

export default connect(mapStateToProps)(toJS(DumbComponent));

在 Redux immutable-js-best-practices 文档部分有很多链接可以继续。