简体   繁体   English

mapStateToProps如何在react-redux实现中接收更新的状态

[英]How can mapStateToProps receive the updated state in react-redux implementation

I'm learning react and redux implementation, and encountered a bug I cannot find a fix for. 我正在学习react和redux的实现,遇到一个我找不到修复程序的错误。 The function mapStateToProps gets the initial (and empty) state. 函数mapStateToProps获取初始(和空)状态。 I'm using seamless-Immutable for the state object, and I know that my reducer is in fact being called and updates the state. 我对状态对象使用了Seamless-Immutable,并且我知道我的reducer实际上正在被调用并更新状态。 I have tried every component-props related methods ( componentWillReceiveProps , componentWillMount , componentDidMount , etc..), and mapStateToProps never receives the updated state; 我尝试了所有与componentWillReceiveProps mapStateToProps相关的方法( componentWillReceivePropscomponentWillMountcomponentDidMount等。),而mapStateToProps永远不会收到更新后的状态; always initial empty state. 始终初始为空状态。 it runs before this.props.dispatch can be called within those methods. 它会在可以在这些方法中调用this.props.dispatch之前运行。

I must be missing something... 我肯定错过了什么...

relevant files: 相关文件:

1. app/index.js 1. app / index.js

import React, { Component } from 'react';
import logo from './logo.svg';
import './index.css';
import PlaylistList from '../../components/PlaylistList';
import { fetchPlaylists } from '../../store/playlists/actions';
import { getPlaylists } from '../../store/playlists/reducer';
import { connect } from 'react-redux';

class App extends Component {
  componentWillReceiveProps() {
    this.props.dispatch(fetchPlaylists();
  }
  render() {
    return (
      <div className="App">
        <div className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h2>Welcome to My Music Store</h2>
        </div>
        <PlaylistList playlists={this.props.playlists}></PlaylistList>
      </div>
    );
  }
}

function mapStateToProps(state) {
  console.dir(state); // prints the initial state (before reducer run)
  return {playlists: getPlaylists(state)}
}

export default connect(mapStateToProps)(App);

2. actions.js: 2. actions.js:

import * as api from '../../services/api';
import * as types from './actionTypes';
import _keyBy from 'lodash/keyBy';

export function fetchPlaylists() {
  return async (dispatch, getState) => {
    try{
      const playlists = await api.fetchPlaylists();
      const playlistsById = _keyBy(playlists, (playlist) => playlist.id);
      dispatch( {  type: types.PLAYLISTS_FETCHED, playlistsById} );
    } catch (error) {
      console.error(error);
    }
  }
}

3. reducer.js: 3. reducer.js:

import * as types from './actionTypes';
import _mapKeys from 'lodash/mapKeys';
import _mapValues from 'lodash/mapValues';
import Immutable from 'seamless-immutable';

const initialState = Immutable({
    playlistsById: {},
    currentPlaylist: undefined
  }
);

export default function reduce(state = initialState, action = {}){
  switch (action.type) {
    case types.PLAYLISTS_FETCHED:
      return state.merge({
        playlistsById: action.playlistsById
      })
    default: return state

  }
}

export function getPlaylists(state) {
  let playlistsById = state.playlistsById;
  let playlistIds = _mapKeys(state.playlistsById);
  console.dir({playlistsById, playlistIds}); // prints empty objects
}

4. reducers.js: 4. reducers.js:

import playlistsReducer from './playlists/reducer';

export { playlistsReducer };

5. actionTypes.js 5. actionTypes.js

export const PLAYLISTS_FETCHED = 'playlists.PLAYLISTS_FETCHED';
export const PLAYLIST_ADDED = 'playlists.PLAYLIST_ADDED';
export const PLAYLIST_REMOVED = 'playlists.PLAYLIST_REMOVED';
export const PLAYLIST_UPDATED = 'playlists.PLAYLIST_UPDATED';
export const FILTER_BY_SEARCH = 'playlists.FILTER_BY_SEARCH';

6. src/index.js 6. src / index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import App from './containers/App';
import './index.css';
import * as reducers from './store/reducers';

const store = createStore(combineReducers(reducers), applyMiddleware(thunk));

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

The problem with your implementation is that in mapStateToProps you expect the state argument to refer a state of your playlistsReducer , while in fact it refers to an entire state object with all your reducers combined by combineReducers function. 实现的问题在于,在mapStateToProps您希望state参数引用您的playlistsReducer的状态,而实际上,它引用的是一个完整的状态对象,其中所有reducers由combineReducers函数组合。

The correct path to the playlistsReducer in the state would be - state.playlistsReducer . 到该状态的playlistsReducer的正确路径是state.playlistsReducer As a result, your mapStateToProps has to look like this: 结果,您的mapStateToProps必须看起来像这样:

function mapStateToProps(state) {
  console.dir(state.playlistsReducer); // prints the initial state (before reducer run)
  return {playlists: getPlaylists(state.playlistsReducer)}
}

By using this mapStateToProps , getPlaylists will receive a correct object with needed data to pass required props to the component. 通过使用此mapStateToPropsgetPlaylists将接收带有所需数据的正确对象,以将所需道具传递给组件。

Another major issue is that getPlaylists method doesn't return anything. 另一个主要问题是getPlaylists方法不返回任何内容。

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

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