简体   繁体   English

mapStateToProps() 在 Redux 应用程序中嵌套状态内的状态?

[英]mapStateToProps() in Redux app nesting state within state?

I've hit a wall trying to figure out why the JSON data I'm fetching and storing on state isn't being mapped to my component props, yet appears when console.log() ed from within the mapStateToProps() function.我想弄清楚为什么我在状态上获取和存储的 JSON 数据没有映射到我的组件道具,但在mapStateToProps()函数中的console.log() ed 时出现。 Am I doing something wrong that I'm not seeing here?我做错了什么,我在这里没有看到?

EDIT: Apparently the properties on state are being mapped to the component but are nested.编辑:显然状态上的属性被映射到组件但嵌套。 The data property, when logged from within mapStateToProps() , has to be accessed via state.state.data . data属性,当从mapStateToProps()记录时,必须通过state.state.data访问。 Prior to this (see reducer), when action.data is logged, the data appears as expected.在此之前(请参阅reducer),当记录action.data时,数据会按预期显示。 No strange nesting.没有奇怪的嵌套。 Perhaps something is wrong with the connect function?或许connect功能有问题?

See how there is a state property populated within the state object:查看如何在 state 对象中填充 state 属性:

查看状态对象中填充的数据属性

Below is my component:下面是我的组件:

class ViewSelector extends Component {
  componentWillMount() {
    this.props.fetchDataAsync('VIEW ALL');
  }

  selectView(event) {
    // this.props.data comes up undefined despite state being mapped
    // to the component props
    console.log('props: ' + this.props.data);
    this.props.filterData(event.target.innerHTML, this.props.data);
  }

  render() {
    return (
      <section>
        <nav>
          <button onClick={this.selectView.bind(this)}>VIEW ALL</button>
          <button onClick={this.selectView.bind(this)}>VIEW LAST WEEK</button>
          <button onClick={this.selectView.bind(this)}>VIEW LAST MONTH</button>
        </nav>
        <Dashboard data={this.props.data}/>
      </section>
    );
  }
}

function mapStateToProps(state) {
  console.log(state);
  // this console.log shows the state with the data property.
  return {
    data: state.data
  };
}

export default connect(mapStateToProps, actions)(ViewSelector);

EDIT: Here is the reducer for clarification:编辑:这是用于澄清的减速器:

import {
  FETCH_DATA,
  FETCH_DATA_SUCCESS,
  FETCH_DATA_FAILURE,
  FILTER_DATA
} from '../actions/types';
import { getLastWeek, getLastMonth } from './reducer_helper';

const initialState = {
  error: '',
  loading: false,
  data: []
};

/* eslint-disable */
const app = (state = initialState, action) => {
  switch(action.type) {
    case FETCH_DATA:
      return { ...state, error: '', loading: true };
    case FETCH_DATA_SUCCESS:
      // console.log(action.data) returns data as expected.
      // no nesting...until it hits the mapStateToProps() function
      return { ...state, error: '', loading: false, data: action.data };
    case FETCH_DATA_FAILURE:
      return { ...state, error: action.error, loading: false };
    case FILTER_DATA:
      let data;
      if (action.view === 'VIEW LAST WEEK') {
        data = getLastWeek(state.data);
      } else if (action.view === 'VIEW LAST MONTH') {
        data = getLastMonth(state.data);
      } else {
        data = state.data;
      }
      return { ...state, error: '', loading: false, data };
  }
  return state;
}

export default app;

Action Creators for clarification on dispatches to Reducer: Action Creators 澄清了对 Reducer 的调度:

export function fetchData() {
  return {
    type: FETCH_DATA
  };
}

export function fetchDataSuccess(data) {
  return {
    type: FETCH_DATA_SUCCESS,
    data: data
  };
}

export function fetchDataFailure(data) {
  return {
    type: FETCH_DATA_FAILURE,
    data
  };
}

export function filterData(view) {
  return {
    type: FILTER_DATA,
    view
  };
}

export function fetchDataAsync() {
  return dispatch => {
    dispatch(fetchData());

    axios.get(DATA_URL)
    .then(res => {
      dispatch(fetchDataSuccess(res.data));
    }).catch(err => {
      dispatch(fetchDataFailure(err));
    });
  };
}

How the entire app is being rendered & the Redux store:整个应用程序的渲染方式和 Redux 商店:

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';

import App from './components/app';
import reducers from './reducers';

const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);

ReactDOM.render(
  <Provider store={createStoreWithMiddleware(reducers)}>
    <App />
  </Provider>
  , document.querySelector('.container'));

After much reviewing, debugging and asking other devs, in a nutshell this is the problem I was facing: The reducer and how it was being imported into a main "manifest" file to manage all other reducers.经过多次审查、调试和询问其他开发人员,简而言之,这就是我面临的问题:reducer 以及如何将其导入主“清单”文件以管理所有其他 reducer。

Basically, since I was only using one reducer in this project, there was no need for a manifest reducer file.基本上,因为我在这个项目中只使用了一个减速器,所以不需要清单减速器文件。 See below:见下文:

import { combineReducers } from 'redux';
import Reducer from './reducer';

const rootReducer = combineReducers({
  state: Reducer
});

export default rootReducer;

I don't know exactly why this was nesting state.我不知道为什么这是嵌套状态。 Perhaps because, theoretically, we would be dealing with multiple reducers with different purposes (like a data reducer and a user reducer) and would need to nest those into our state tree to manage.也许是因为,理论上,我们将处理多个具有不同目的的 reducer(如数据 reducer 和用户 reducer),并且需要将它们嵌套到我们的状态树中进行管理。

state = {
  // here is one nested reducer with its own properties
  data: {
    type: // somevalue,
    filtered: true
  },
  // here is another
  user: {
    name: // somename,
    password: // somepassword
  }
};

In short...be mindful of how your reducer is being imported into your createStore function and if it's the only reducer being used or if there's multiple reducers being run through a manifest file of sorts.简而言之...请注意您的减速器是如何导入到您的createStore函数中的,以及它是否是唯一使用的减速器,或者是否有多个减速器正在通过各种清单文件运行。

Cheers.干杯。

So your mapStateToProps value of data is returning an object.所以你的mapStateToProps data值正在返回一个对象。 You probably want this instead:你可能想要这个:

function mapStateToProps(state) {
  console.log(state);
  // this console.log shows the state with the data property.
   return {
     ...state.app
   };
 }

This will give you access to the fields in your data reducer.这将使您可以访问data缩减器中的字段。 Also, it's not clear from the example exactly how you will use the data, or what fields are included in it.此外,示例中并不清楚您将如何使用数据,或者其中包含哪些字段。 Also, to start with, your reducer is not initialized to null or empty values for the fields it will use, which can definitely cause errors.此外,首先,您的reducer 没有初始化为它将使用的字段的空值或空值,这肯定会导致错误。 Probably better to start with an initialState object:initialState对象开始可能更好:

const initialState = {
  error: '',
  loading: false,
  data: []
};

const app = (state=initialState, action) => {
 /* .... */

I was having a similar issue with mapStateToProps , where on the first render it would nest the initial state in an object with the key of state.我在mapStateToProps中遇到了类似的问题,在第一次渲染时,它会将初始状态嵌套在具有状态键的对象中。 When I updated the state using a dispatch method it would create another key-value pair.当我使用调度方法更新状态时,它会创建另一个键值对。 (The below entry in the console is when I console log state inside the mapStateToProps function). (控制台中的以下条目是当我在 mapStateToProps 函数中控制台日志状态时)。

Initial State which is nested:嵌套的初始状态:
嵌套的初始状态

I released that in my reducer function, I was returning state for my default case.我在我的 reducer 函数中发布了它,我正在为我的默认情况返回状态。

const initialState = {
    stateprop1: false,
    user_text: "empty",
    };

    const reducer1 = (state = initialState, action)             => {
    switch (action.type) {
        case ACTION_TYPES.SUCCESS:
            return {
                ...state,
                stateprop1: true,
            };
        case ACTION_TYPES.FAILURE:
            return {
                ...state,
                stateprop1: false,
            };
        case ACTION_TYPES.USER_INPUT:
            return {
                ...state,
                user_text: action.payload,
            };
        default:
            return {
                state,
            };
    }
};

​If, instead, I spread the state in the default case, the nesting behaviour wasn't observed.相反,如果我在默认情况下传播状态,则不会观察到嵌套行为。

default:
    return {
        ...state,
    };

So now if I console log state in the mapStateToProps function I get所以现在如果我在mapStateToProps函数中控制台日志状态我得到

State that isn't nested :未嵌套的状态:
未嵌套的状态

I hope this helps.我希望这会有所帮助。

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

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