React 组件不从 Redux Store 呈现新数据

React Component not Rendering New Data from Redux Store

出现这个问题是因为在我通过 redux 存储形式的 websocket 获取初始状态之前调用了我的高级组件。因此,当我的组件处理状态时,它们只接收默认状态,即空的不可变 Map()。但是,在某些时候我从服务器接收到商店,我从我的 action_creators 调用 setState 并且由我的 reducer 处理,它将商店合并到我的本地商店中。使用 redux-logger 记录整个过程。请参阅相关的屏幕截图。您可以看到本地状态已更改,但组件没有重新渲染。

Redux-logger output <- 组件状态是高级组件接收的内容。

我知道我的 actions 和 reducer 在某种程度上起作用,因为如果我将本地 JSON 文件与我的本地存储合并,组件接收的状态不仅是一个空 Map,而且包含完整的状态数据。然而,这并不意味着重新渲染有效,我假设状态只是在处理,因为它在组件最初被调用之前可用。

请看我的index.js、action_creators.js、reducer.js和我的高级组件Game

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import {createStore, applyMiddleware} from 'redux';
import {Provider} from 'react-redux';
import {setState} from './action_creators';
import io from 'socket.io-client';
import logger from 'redux-logger';

import routes from './routes.js';
import reducer from './reducer';
import InitialState from './initial.json';
import './index.scss';

const socket = io(`${location.protocol}//${location.hostname}:8090`);
const store = createStore(
  reducer,
  applyMiddleware(logger)
);

socket.on('state', state => store.dispatch(setState(state)));
// socket.on('state', state => console.log("From Store", store.getState()));
// socket.on('state',store.dispatch(setState(InitialState)));
// console.log("From JSON",InitialState);

ReactDOM.render(
<Provider store={store}>
    {routes}
</Provider>, document.getElementById('root'));

reducer.js

import {Map} from 'immutable';

export default function(state = Map(), action) {
  switch (action.type) {
    case 'SET_STATE':
      return state.merge(state, action.state);
    default:
      return state;
  }
}

action_creators.js

export function setState(state) {
  return {type: 'SET_STATE',state};
}

export function modIndicator(state, playerId, indicator, modifier) {
  return {type: 'INDICATORS', playerId: playerId, indicator: indicator, modifier: modifier};
}
export function modCollection(state, collection, property, value, op, category) {
  return {
    type:'COLLECTIONS',
    collection:collection,
    property:property,
    value:value,
    op:op,
    category:category};
}

Game.js

import React, {PureComponent, PropTypes} from 'react';
// import Card from '../../Components/Card/index.js';
// import Collection from '../../Components/Collection/index.js';
import Indicator from '../../Components/Indicator/index.js';
import Counter from '../../Components/Counter/index.js';
import {connect} from 'react-redux';
import * as actionCreators from './../../action_creators.js';
import './styles.scss';

export default class Game extends PureComponent {
  render() {
let playersById = this.props.playersById;
let collections = this.props.collections;
let counters = this.props.counters;

let indic_list = [];
let coll_list = [];

//Indicators
playersById.forEach(function(value, key, map){
  indic_list.push(<p className="section-name">{playersById.get(key).get('name')+"'s Indicators"}</p>)
  playersById.get(key).get('indicators').forEach(function(value, key, map){
      indic_list.push(<Indicator label={key}>{value}</Indicator>)
  });
});

//Collections
collections.forEach(function(value, key, map) {
  coll_list.push(<span>{key}: {collections.get(key).get("content").size}</span>);
  collections.get(key).get("control").forEach(function(value, key, map){
      coll_list.push(<span>Control: {value}</span>);
  });
});

return (
    <div className="frame">
      <div className="left-col">
        {indic_list}
      </div>
      <div className="center-col">
        <span className="collections">
          {coll_list}
        </span>
      </div>
      <div className="right-col">
        <div className="counters">
          {counters.map(function(type){
            return <Counter label={type}></Counter>
          })}
        </div>
      </div>
    </div>
  )
  }
}

Game.PropTypes = {
  playersById:  PropTypes.object.isRequired,
  collections: PropTypes.object.isRequired,
  counters: PropTypes.object.isRequired
}
function mapStateToProps(state) {
  console.log("Component State",state);
  return {
    playersById: state.get('playersById'),
    collections: state.get('collections'),
    counters: state.get('counters')
  };
}
export const GameContainer = connect(mapStateToProps, actionCreators)(Game);

您有 3 个选择:

1。设置您的组件可以使用的初始状态

import {fromJS} from 'immutable';

const initialState = fromJS({
    playersById: [],
    collections: [],
    counters: []
});

export default function(state = initialState, action) {
  switch (action.type) {
    case 'SET_STATE':
      return state.merge(state, action.state);
    default:
      return state;
  }
}

2。如果数据缺失,请提供一些合适的数据

import {List} from 'immutable';

function mapStateToProps(state) {
  console.log("Component State",state);
  return {
    playersById: state.get('playersById') || new List(),
    collections: state.get('collections') || new List(),
    counters: state.get('counters') || new List()
  };
}

这可能会更安全,因为您的状态每次都会被完全替换,因此如果传入数据不适合呈现,这将保护您的组件

3。让组件处理丢失的数据

render() {

if (!(playersById && collections && counters)) {
    return null; // or a loading spinner?
}

// ... the rest of your render function

}