商店的React / Redux props属性未定义

时间:2018-06-29 23:39:59

标签: javascript reactjs redux

在我的应用中,我正在使用:

"devDependencies": {
    "babel-plugin-transform-class-properties": "^6.24.1",
    "babel-preset-es2016": "^6.24.1",
    "babel-preset-react": "^6.24.1",
    "bootstrap": "^4.0.0",
    "browser-sync": "^2.24.5",
    "browser-sync-webpack-plugin": "^2.2.2",
    "cross-env": "^5.1",
    "jquery": "^3.2",
    "laravel-mix": "^2.0",
    "popper.js": "^1.12",
    "react": "^16.4.1",
    "react-dom": "^16.4.1",
    "react-redux": "^5.0.7",
    "react-router-dom": "^4.3.1",
    "redux": "^4.0.0",
    "redux-thunk": "^2.3.0",
    "uuid": "^3.3.2"
},

在redux端,我有以下设置: store.js:

import {createStore, applyMiddleware} from 'redux';
import thunk from 'redux-thunk';

import rootReducer from './reducers';
const initialState = {};
const middleware = [thunk];

const store = createStore(rootReducer, initialState, applyMiddleware(...middleware));

export default store;

rootReducer是:

import {combineReducers} from 'redux';

import menuReducer from './menuReducer';

const rootReducer = combineReducers({
  menu: menuReducer
});

export default rootReducer;

其中只有menuReducer可供使用:

import * as types from '../actions/types';

const initialState = {
  data: {}
}

export default function(state = initialState, action) {
  switch (action.type) {
    case types.GET_MENU:
      return {
        ...state,
        data: action.payload
      }
    default:
      return state;
  }
}

通过单个菜单操作即可获取供稿:

import * as types from './types';

export const getMenu = () => (dispatch) => {
  fetch('/admin/json/menu.json')
  .then(res => res.json())
  .then(data => dispatch({
      type: types.GET_MENU,
      payload: data
    })
  )
}

最后是我在组件方面的信息: App.js:

import React from 'react';
import {Provider} from 'react-redux';
import {BrowserRouter} from 'react-router-dom';

import store from '../store';
import SideMenu from './sidemenu';
import Content from './content';

class App extends React.Component {
  render() {
    return(
      <Provider store={store}>
        <BrowserRouter basename='/yonetim'>
          <div className="h-100">
            <SideMenu className="h-100"/>
            <Content className="h-100"/>
          </div>
        </BrowserRouter>
      </Provider>
    );
  };
};

export default App;

以及尝试使用此基本redux实现的组件:

import React, {Fragment} from 'react';
import {v1 as uuid} from 'uuid';
import {connect} from 'react-redux';

import {getMenu} from '../actions/menuActions';
import MenuDropdown from './parts/menu-dropdown';
import MenuItem from './parts/menu-item';

class SideMenu extends React.Component {
  componentWillMount() {
    this.props.getMenu();
  }
  render() {
    console.log(this.props.menuData);
    return(
      <header>
        <div className="cnt-menu-head">
          <h6>{this.props.menuData.heading}</h6>
        </div>
        <div className="cnt-menu">
          <ul className="nav nav-pills flex-column">
            {this.props.menuData.sections.map((section, i) => {
              if (section.type === 'link') {
                let key = uuid();
                return <MenuItem key={key} exact={section.exact} linkTo={section.linkTo} linkText={section.linkText}/>
              } else if (section.type === 'dropdown') {
                let keyedLinks = section.links.map((link) => {
                  link.key = uuid();
                  return link;
                });
                return (
                  <Fragment key={uuid()}>
                    <div className="horizontal-separator"></div>
                    <MenuDropdown linkText={section.linkText} links={keyedLinks}/>
                  </Fragment>
                )
              }
            })}
          </ul>
        </div>
      </header>
    );
  };
};

function mapStateToProps(state) {
  return {
    menuData: state.menu.data
  }
}

export default connect(mapStateToProps, {getMenu})(SideMenu);

会发生什么:

我得到了错误:

  

未捕获的TypeError:无法读取未定义的属性“ map”

参考我的代码中的this.props.menuData.sections.map()部分。现在有趣的是:

  1. 该组件的render函数被调用两次
  2. 当我在动作和减速器中同时使用console.log()时,我看到它们也被两次调用。
  3. 当我注释掉试图在this.props.menuData中使用render()的代码时,渲染功能开始处的console.log(this.props.menuData);render()的第一次运行中打印为空,然后被第二次运行时要使用的实际数据充满。我完全不知道这段代码是怎么回事,或者可能是潜在的原因。任何帮助深表感谢。谢谢。

2 个答案:

答案 0 :(得分:4)

这里的问题是您正在调用“异步”操作(获取),该操作在获取后分派数据。

因此,将加载您的组件,但是当您的函数(getMenu)将操作分派给Redux时,您的组件将第二次运行。

我可能建议做一个三元处理,它调节menuData使用者的渲染并显示一个加载器:

在渲染器内部:

return this.props.menuData.sections ? (actualCodeGoesHere) : <Loader />

答案 1 :(得分:1)

请确保您没有第一次渲染的数据。有两种方法可以解决此问题: 1.将小节的键设置为menuReducer的initialState。 2.在this.props.menuData.sections.map(…)之前添加一个检测。也许是这样的:this.props.menuData.sections && this.props.menuData.sections.map(…)