反应Redux映射状态到道具不起作用

时间:2018-09-09 19:55:53

标签: javascript reactjs redux

所以我正在尝试学习Redux的React,到目前为止,我认为我已经能够解决使它工作所需的大部分代码,但是在将状态传递给我的过程中存在问题零件。我正在使用Visual Studio 2017的ASP.NET Core项目模板,该模板具有react和redux样板代码,他们使用了以下代码:

export default connect(
  state => state.weatherForecasts,
  dispatch => bindActionCreators(actionCreators, dispatch)
)(FetchData);

我尝试用自己的组件做同样的事情,像这样:

export default connect(
  state => state.lecture,
  dispatch => bindActionCreators(actionCreators, dispatch)
)(LectureTable);

但是当尝试访问道具的内容时,我想要获取的属性被标记为未定义。我通过Redux devtools检查了我的初始状态是否存在,但是我的组件无法看到我要传递给它的道具。奇怪的是,我只是模仿了样板代码,但它不起作用,但样板代码工作得很好(即,我可以转到组件并注销其初始状态)。

由于我遵循的是Visual Studio使用的格式,所以我的动作创建者,化简器和常量位于以下所示的一个文件中:

const GET_LECTURES = "GET_LECTURES";

const initialState = {
    lectures: [],
    selectedLecture: {},
    isLoading: false,
    test: 0
};

export const actionCreators = {
    requestLectures: isLoading => async (dispatch) => 
    {    
      if (!isLoading) {
        // Don't issue a duplicate request (we already have or are loading the requested data)
        return;
      }

      dispatch({ type: GET_LECTURES });

      const url = `api/lecture/`;
      const response = await fetch(url);
      const lectures = await response.json();

      dispatch({ type: RECEIVE_LECTURES, payload: lectures });
    } 
  };

export const reducer = (state = initialState, action) => {
    switch (action.type) {
    case GET_LECTURES:
        return { ...state, isLoading: true }; 
        default:
        return state;
    }
};

对不起,如果一切都很混乱。我真的才刚刚开始了解redux。

修改 我的组件代码:

import React, { Component } from 'react';
import {Button, Table, Label, Menu, Icon} from 'semantic-ui-react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import {actionCreators} from './../../store/Lecture';

export class LectureTable extends Component {

  componentWillMount(){
   // this.props.requestLectures(this.props.isLoading);
    console.log(this.props.test);
  }

  render() {
    return (
        <Table size='large'>
        {/*removed to make it cleaner..currently only has static data too lol*/}
      </Table>
    )
  }
}



export default connect(
  state => state.lecture,
  dispatch => bindActionCreators(actionCreators, dispatch)
)(LectureTable);

配置我的商店的位置

import { applyMiddleware, combineReducers, compose, createStore } from 'redux';
import thunk from 'redux-thunk';
import { routerReducer, routerMiddleware } from 'react-router-redux';
import * as Lecture from './Lecture';
import * as Counter from './Counter';
import * as WeatherForecasts from './WeatherForecasts';

export default function configureStore(history, initialState) {
  const reducers = {
    lecture: Lecture.reducer,
    counter: Counter.reducer,
    weatherForecasts: WeatherForecasts.reducer
  };

  const middleware = [
    thunk,
    routerMiddleware(history)
  ];

  // In development, use the browser's Redux dev tools extension if installed
  const enhancers = [];
  const isDevelopment = process.env.NODE_ENV === 'development';
  if (isDevelopment && typeof window !== 'undefined' && window.devToolsExtension) {
    enhancers.push(window.devToolsExtension());
  }

  const rootReducer = combineReducers({
    ...reducers,
    routing: routerReducer
  });

  return createStore(
    rootReducer,
    initialState,
    compose(applyMiddleware(...middleware), ...enhancers)
  );
}

我的index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'react-router-redux';
import { createBrowserHistory } from 'history';
import configureStore from './store/configureStore';
import App from './pages/App';
import registerServiceWorker from './registerServiceWorker';

// Create browser history to use in the Redux store
const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href');
const history = createBrowserHistory({ basename: baseUrl });

// Get the application-wide store instance, prepopulating with state from the server where available.
const initialState = window.initialReduxState;
const store = configureStore(history, initialState);

const rootElement = document.getElementById('root');

ReactDOM.render(
  <Provider store={store}>
    <ConnectedRouter history={history}>
      <App />
    </ConnectedRouter>
  </Provider>,
  rootElement);

registerServiceWorker();

5 个答案:

答案 0 :(得分:2)

connect()的第一个参数应该是一个返回对象的函数-将要添加的道具作为键,其值是state的值。例如

state => ({ lecture: state.lecture })

答案 1 :(得分:1)

在他们的示例中,我认为weatherForecasts是一个对象。在您的示例中,lectures似乎是一个数组,因此,如果您只需要获取mapStateToProps属性

,我建议像这样重写lectures函数
state => ({ lectures: state.lectures})

如果您需要整个state,则可以拥有state => state,以便可以访问道具this.props.testthis.props.lectures

请记住,mapStateToProps应该返回object,而不是array。顺便说一下,在化简器中,字段名是lectures(复数)而不是lecture,所以state => state.lecture将是undefined

答案 2 :(得分:1)

Rick,您的connect参数应该类似于:

export default connect( state => {
    return {
      test: state.lecture // Or any value
    }
})(LectureTable);

您正在尝试控制台记录test道具,因此您应该将其包括在连接呼叫中。

答案 3 :(得分:1)

我找到了解决方案。首先,我对堆栈溢出和反应都无知,所以我为所有不一致之处(如果是正确的词?)提出了要求。

我发现的内容:

  1. 我正在使用React Router
  2. 我正在对路由器渲染的组件的子组件执行connect方法
  3. 我将connect方法放置到父组件上,并且有效

一些注意事项:

  • state => state.lecture仍然有效
  • 我会牢记您的所有建议,并相应地更改我的代码
  • 我坚持使用所拥有的代码来解决问题的唯一原因是,因为我不能接受这样的事实,除非我做了与样板完全不同的事情,否则样板代码将无法工作。我只是没有考虑到路由器在其中扮演了重要角色。
  • 我再说一遍...我是一个反应型菜鸟,很抱歉浪费您的时间T_T

再次编辑: 我能够将另一个子组件与Redux存储连接。我试图看看为什么我仍然无法对导致我问这个问题的特定组件执行此操作。找到原因后,我将更新答案。

答案 4 :(得分:0)

您在这里有两个问题。

您将mapStateToProps函数定义为connect错误的第一个参数。正如许多答案所解释的那样,现在您应该像这样使用它:

export default connect(
  state => ( { lecture: state.lecture } ),
  dispatch => bindActionCreators(actionCreators, dispatch)
)(LectureTable);

现在,您有一个lecture道具作为状态。您可以使用this.props.lecture到达它。但是,在您的componentWillMount方法中,您尝试像this.props.test一样记录它。应该是this.props.lecture.test

顺便说一句,尝试使用componentDidMount代替componentWillMount,因为它将在将来的版本中弃用。