【问题标题】:React-Redux program returns "TypeError: Cannot read property 'map' of undefined"React-Redux 程序返回“TypeError: Cannot read property 'map' of undefined”
【发布时间】:2018-08-12 21:52:51
【问题描述】:

我正在学习 react-redux,我们只是想从后端引入一个基本对象。我的商店目前是空的,我已经尽可能地按照我正在使用的教程进行操作。

文件 1:“reducer-books.js”。这个文件只是定义了我想要存储并传递给全局“状态”的内容。

export default function() {
    return [
      { title: "Javascript: The Good Parts", pages: 101 },
      { title: "Harry Potter", pages: 39 },
      { title: "The Dark Tower", pages: 85 },
      { title: "Eloquent Ruby", pages: 1 }
    ;
  }

文件 2:“index.js”。这里我将上面定义的 Books Reducer 通过 combine reducer 放入 state 对象中(对吧?)。

import { combineReducers } from "redux";
import { BooksReducer } from "./reducer-books";

//Reducer = a function that returns a piece of the application state.
//Just a function that does that. Period.
//Because the applicaiton can have many different pieces of state, we
//can have MANY reducers.

const rootReducer = combineReducers({
  books: BooksReducer
});

export default rootReducer;

文件 3:“book-list.js”,我想在其中引入这些数据。但我试图打印出状态,它只是空的;~;好困惑..我喜欢检查地图状态到道具功能,它应该可以工作吗?

import React, { Component } from "react";

import { connect } from "react-redux"; // redact-redux is the glue between these two!

class BookList extends Component {
    renderList() {

        console.log("hmm?");
        console.log(this.props);
        console.log("HMMM?");
        console.log(this.props.book);
        console.log(this.props.books);
        console.log("HMMM?3333");
        console.log("HMMM?");

        return this.props.books.map((book) => {
            return (
                <li key={book.title} className="list-group-item">
                    {book.title}
                </li>
            );
        });


    render() {
        return (
            <ul className="list-group col-sm-4">
                {this.renderList()}
            </ul>
        )
   }
}


function mapStateToProps(state) {
    console.log("below hm?");
    console.log(state);
    return {
        books: state.books
    }
}

export default connect(mapStateToProps)(BookList);

文件 4:app.js

import React from "react";
import { Component } from "react";

import BookList from "../containers/book-list";

export default class App extends Component {
  render() {
      return (
          <div>
            <BookList />
          </div>
        );
      }
    }

文件5:index.js(我猜这个文件是“根文件”?

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

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

//Reducer = a function that returns a piece of the application state.
//Just a function that does that. Period.
//Because the applicaiton can have many different pieces of state, we 
//can have MANY reducers.
const createStoreWithMiddleware = applyMiddleware()(createStore);

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

这是我从这个错误中得到的控制台输出:

bundle.js:21363 No reducer provided for key "books"
warning @ bundle.js:21363
combineReducers @ bundle.js:21448
Object.defineProperty.value @ bundle.js:22554
__webpack_require__ @ bundle.js:20
(anonymous) @ bundle.js:72
__webpack_require__ @ bundle.js:20
(anonymous) @ bundle.js:47
__webpack_require__ @ bundle.js:20
(anonymous) @ bundle.js:40
(anonymous) @ bundle.js:43
bundle.js:21363 Store does not have a valid reducer. Make sure the argument     
passed to combineReducers is an object whose values are reducers.
warning @ bundle.js:21363
combination @ bundle.js:21481
dispatch @ bundle.js:21269
createStore @ bundle.js:21344
(anonymous) @ bundle.js:21611
(anonymous) @ bundle.js:86
__webpack_require__ @ bundle.js:20
(anonymous) @ bundle.js:47
__webpack_require__ @ bundle.js:20
(anonymous) @ bundle.js:40
(anonymous) @ bundle.js:43
bundle.js:22503 below hm?
bundle.js:22504 {}
bundle.js:22462 hmm?
bundle.js:22463 {books: undefined, dispatch: ƒ}
bundle.js:22464 HMMM?
bundle.js:22465 undefined
bundle.js:22466 undefined
bundle.js:22467 HMMM?3333
bundle.js:22468 HMMM?
bundle.js:22462 hmm?
bundle.js:22463 {books: undefined, dispatch: ƒ}
bundle.js:22464 HMMM?
bundle.js:22465 undefined
bundle.js:22466 undefined
bundle.js:22467 HMMM?3333
bundle.js:22468 HMMM?
bundle.js:22470 Uncaught TypeError: Cannot read property 'map' of undefined
    at BookList.renderList (bundle.js:22470)
    at BookList.render (bundle.js:22484)
    at finishClassComponent (bundle.js:11048)
    at updateClassComponent (bundle.js:11016)
    at beginWork (bundle.js:11641)
    at performUnitOfWork (bundle.js:14473)
    at workLoop (bundle.js:14502)
    at HTMLUnknownElement.callCallback (bundle.js:2759)
    at Object.invokeGuardedCallbackDev (bundle.js:2797)
    at invokeGuardedCallback (bundle.js:2846)
renderList @ bundle.js:22470
render @ bundle.js:22484
finishClassComponent @ bundle.js:11048
updateClassComponent @ bundle.js:11016
beginWork @ bundle.js:11641
performUnitOfWork @ bundle.js:14473
workLoop @ bundle.js:14502
callCallback @ bundle.js:2759
invokeGuardedCallbackDev @ bundle.js:2797
invokeGuardedCallback @ bundle.js:2846
replayUnitOfWork @ bundle.js:13977
renderRoot @ bundle.js:14544
performWorkOnRoot @ bundle.js:15108
performWork @ bundle.js:15029
performSyncWork @ bundle.js:15006
requestWork @ bundle.js:14906
scheduleWorkImpl @ bundle.js:14781
scheduleWork @ bundle.js:14741
scheduleRootUpdate @ bundle.js:15369
updateContainerAtExpirationTime @ bundle.js:15397
updateContainer @ bundle.js:15424
ReactRoot.render @ bundle.js:18728
(anonymous) @ bundle.js:19147
unbatchedUpdates @ bundle.js:15216
legacyRenderSubtreeIntoContainer @ bundle.js:19143
render @ bundle.js:19202
(anonymous) @ bundle.js:84
__webpack_require__ @ bundle.js:20
(anonymous) @ bundle.js:47
__webpack_require__ @ bundle.js:20
(anonymous) @ bundle.js:40
(anonymous) @ bundle.js:43
bundle.js:12302 The above error occurred in the <BookList> component:
    in BookList (created by Connect(BookList))
    in Connect(BookList) (created by App)
   in div (created by App)
    in App
    in Provider


Visit <redacted>
logCapturedError @ bundle.js:12302
logError @ bundle.js:12341
commitErrorLogging @ bundle.js:12554
commitAllLifeCycles @ bundle.js:14118
callCallback @ bundle.js:2759
invokeGuardedCallbackDev @ bundle.js:2797
invokeGuardedCallback @ bundle.js:2846
commitRoot @ bundle.js:14253
completeRoot @ bundle.js:15161
 @ bundle.js:15111
performWork @ bundle.js:15029
performSyncWork @ bundle.js:15006
requestWork @ bundle.js:14906
scheduleWorkImpl @ bundle.js:14781
scheduleWork @ bundle.js:14741
scheduleRootUpdate @ bundle.js:15369
updateContainerAtExpirationTime @ bundle.js:15397
updateContainer @ bundle.js:15424
ReactRoot.render @ bundle.js:18728
(anonymous) @ bundle.js:19147
unbatchedUpdates @ bundle.js:15216
legacyRenderSubtreeIntoContainer @ bundle.js:19143
render @ bundle.js:19202
(anonymous) @ bundle.js:84
__webpack_require__ @ bundle.js:20
(anonymous) @ bundle.js:47
__webpack_require__ @ bundle.js:20
(anonymous) @ bundle.js:40
(anonymous) @ bundle.js:43
bundle.js:22470 Uncaught TypeError: Cannot read property 'map' of undefined
at BookList.renderList (bundle.js:22470)
at BookList.render (bundle.js:22484)
at finishClassComponent (bundle.js:11048)
at updateClassComponent (bundle.js:11016)
at beginWork (bundle.js:11641)
at performUnitOfWork (bundle.js:14473)
at workLoop (bundle.js:14502)
at renderRoot (bundle.js:14533)
at performWorkOnRoot (bundle.js:15108)
at performWork (bundle.js:15029)
renderList @ bundle.js:22470
render @ bundle.js:22484
finishClassComponent @ bundle.js:11048
updateClassComponent @ bundle.js:11016
beginWork @ bundle.js:11641
performUnitOfWork @ bundle.js:14473
workLoop @ bundle.js:14502
renderRoot @ bundle.js:14533
performWorkOnRoot @ bundle.js:15108
performWork @ bundle.js:15029
performSyncWork @ bundle.js:15006
requestWork @ bundle.js:14906
scheduleWorkImpl @ bundle.js:14781
scheduleWork @ bundle.js:14741
scheduleRootUpdate @ bundle.js:15369
updateContainerAtExpirationTime @ bundle.js:15397
updateContainer @ bundle.js:15424
ReactRoot.render @ bundle.js:18728
(anonymous) @ bundle.js:19147
unbatchedUpdates @ bundle.js:15216
legacyRenderSubtreeIntoContainer @ bundle.js:19143
render @ bundle.js:19202
(anonymous) @ bundle.js:84
__webpack_require__ @ bundle.js:20
(anonymous) @ bundle.js:47
__webpack_require__ @ bundle.js:20
anonymous) @ bundle.js:40
(anonymous) @ bundle.js:43
contentscript.js:58 <body>​…​</body>​

【问题讨论】:

  • 欢迎来到 StackOverflow!您是否介意将您的 console.log 语句的输出包括在内,以便我们帮助调试。谢谢!
  • 我可以帮你放松一下,我可以看看你的 Root 组件(你在哪里初始化你的商店)?请问?
  • Erty Seidohl 抱歉耽搁了!现在将其添加到帖子中:)! Kahlid Ahmada 也添加到帖子中,我相信至少是这个文件,我没有写这个,据我记得我们没有在教程中复习它:(!

标签: javascript reactjs redux react-redux


【解决方案1】:

这会让你继续前进:

组件/app.js:

import React from "react";
import { Component } from "react";

import BookList from "../containers/book-list";

export default class App extends Component {
  render() {
    return (
        <div>
          <BookList />
        </div>
      );
  }
}

容器/book-list.js:

import React, { Component } from "react";
import { connect } from "react-redux"; // redact-redux is the glue between these two!

class BookList extends Component {
    renderList() {
        return this.props.books.map((book) => {
            return (
                <li key={book.id} className="list-group-item">
                    {book.title}
                </li>
            );
        });
    }

    render() {
        return (
            <ul className="list-group col-sm-4">
                {this.renderList()}
            </ul>
        )
    }
}

const mapStateToProps = (state) => ({
    books: state.books
});

export default connect(mapStateToProps)(BookList);

index.js:

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

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

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

reducer-books.js:

const reducer = () => {
  return [
    { id: 1, title: 'Javascript: The Good Parts', pages: 101 },
    { id: 2, title: 'Harry Potter', pages: 39 },
    { id: 3, title: 'The Dark Tower', pages: 85 },
    { id: 4, title: 'Eloquent Ruby', pages: 1 }
  ];
};

export default reducer;

reducers.js:

import { combineReducers } from "redux";
import BooksReducer from "./reducer-books";

//Reducer = a function that returns a piece of the application state.
//Just a function that does that. Period.
//Because the applicaiton can have many different pieces of state, we
//can have MANY reducers.

const rootReducer = combineReducers({
  books: BooksReducer
});

export default rootReducer;

【讨论】:

  • 您好,感谢您的回复:)!我尝试在文件顶部添加此导​​入语句,遗憾的是它没有更改错误消息。我在问题中添加了我的 index.js 文件,我很抱歉我的菜鸟!
  • @KeepingItAnonymous 不用担心。您的代码有一些问题,我在上面发布的代码中修复了它们。您应该能够将其复制到您的项目文件中并开始运行
  • 谢谢!这完美地解决了这个问题:) 一切都按预期出现在屏幕上!我将查看代码以查找错误位置(立即想到的是 reducer-books.js 的 const reducer)。
  • @KeepingItAnonymous 很高兴听到。如果答案为您提供所需的信息,请友好提醒您投票并标记为已接受
【解决方案2】:

如果我没记错的话,这是来自 Stephen Grider Udemy 的课程,是吗?我做了这个确切的课程,我可以帮助你。

文件 1:不幸的是,这不是设置减速器的方法。 reducer 只是简单地“监听”一个动作,然后根据动作返回一个新的状态。对于像这样的简单应用程序,我们也不需要使用组合减速器。我们可以只使用一个 rootReducer。你的 reducer 应该是这样的:

import * as ACTION_TYPES from '../actions/action_types';

const initialState = {
  books: []
}

const rootReducer = (state = initialState, action) => {
  switch (action.type) {
    case ACTION_TYPES.SET_BOOKS:
      return  {
        ...state,
        books: action.payload 
      }
    case ACTION_TYPES.REMOVE_BOOKS:
      return {
        ...state,
        books: []
      }
    default:
      return state
    }
}

export default rootReducer;

现在我们需要对 reducer 的每个 case 语句进行两个不同的操作。一个用于删除书籍,一个用于将书籍设置为 redux 状态。在 action_types.js 文件中声明两种操作类型,如下所示:

export const SET_BOOKS = "SET_BOOKS"

export const REMOVE_BOOKS = "REMOVE_BOOKS" 

现在在单独的 actions.js 文件中创建将与 reducer 一起使用的操作。

import * as ACTION_TYPES from './action_types';


{type: ACTION_TYPES.SET_BOOKS}

{type: ACTION_TYPES.REMOVE_BOOKS}


export function set_books(books) {
  return dispatch => {
      dispatch({type: ACTION_TYPES.SET_BOOKS, payload: books})
  }
}

export function remove_books() {
  return dispatch => {
      dispatch({type: ACTION_TYPES.REMOVE_BOOKS})
  }
}

请注意 set_books() 函数上的“有效负载:书籍”属性。这将是我们通过 React 容器将书籍保存到全局 redux 状态的主要方式。

现在创建一个容器,我们将使用该容器将书籍设置为全局 redux 状态。像这样创建一个容器:

import React, { Component } from 'react'
import { connect } from 'react-redux';
import * as ACTION_TYPES from '../store/actions/action_types';
import * as ACTIONS from '../store/actions/actions';



class Container extends Component {
  books = [
     { title: "Javascript: The Good Parts", pages: 101 },
      { title: "Harry Potter", pages: 39 },
      { title: "The Dark Tower", pages: 85 },
      { title: "Eloquent Ruby", pages: 1 }
  ]

  renderBooks = (props) => (
    <div>
      <p> {props.book.title} </p>
      <p> {props.book.pages} </p>
    </div>
  )

   render() {
    return (
        <div>

             <button onClick={() => this.props.action1() } > Dispatch Action 1 </button>
             <button onClick={() => this.props.action2() } > Dispatch Action 2 </button>
             <button onClick={() => this.props.action_middleware1(this.books) } > Dispatch Async Middleware Action 1 </button>
             <button onClick={() => this.props.action_middleware2() } > Dispatch Async Middleware Action 2 </button>
             { this.props.books
               ? this.props.books.map(book =>
               <this.renderBooks key={book.title} book={book} />)
               : null
             }
       </div>
    )
  }
}


function mapStateToProps(state) {
    return {
        books: state.books
    };
}

function mapDispatchToProps (dispatch) {
  return {
    action1: () => dispatch({type: ACTION_TYPES.SET_BOOKS}),
    action2: () => dispatch({type: ACTION_TYPES.REMOVE_BOOKS}),
    action_middleware1: (books) => dispatch(ACTIONS.set_books(books)),
    action_middleware2: () => dispatch(ACTIONS.remove_books())
  }
}


export default connect(mapStateToProps, mapDispatchToProps)(Container); 

注意 mapDispatchtoProps 函数的 action_middleware1 属性。这就是我们将书籍保存到全局状态的方式。为了真正将书籍渲染到屏幕上,我们使用三元表达式,因为它是异步的。您还可以使用“Dispatch Async Middleware Action 2”从全局状态中删除书籍,这将自动将书籍从渲染中删除到屏幕上。

当您单击“Dispatch Async Middleware Action 1”按钮时,我们初始化的“books”对象将作为“payload”属性传递给操作,然后通过 reducer 保存到全局状态。一旦它被保存到全局状态,我们的“this.props.books”三元表达式将为真,书籍将自动呈现到屏幕上。我们的“this.props.books”来自我们定义的 mapStatetoProps 函数,我们的容器通过我们在容器文件最底部使用的“connect”函数知道全局 redux 状态。

如果还没有完成,你应该像这样设置根 index.js 文件:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

import { Provider } from 'react-redux';
import rootReducer from './store/reducers/reducer';
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';



let store = createStore(
                        rootReducer,
                        applyMiddleware(thunk)
                      )


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

点击 Dispatch Async Middleware 1 按钮之前

点击 Dispatch Async Middleware 1 按钮后

就是这样!这就是使您的应用程序运行所需的所有代码。

编辑:我将 app.js 文件保留为默认的“create-react-app”

这是我做的一个非常相似的应用程序的回购链接:

https://github.com/iqbal125/react-redux-starter

【讨论】:

  • 谢谢!不幸的是,这个回复仍然高于我的技能水平,但我可以说这是解决我当前问题的更好方法,我完全欣赏你所涉及的深度和细节水平,包括链接你自己的回购 :)!希望我能在教程中达到这一点,从而更好地理解这一点!谢谢!
猜你喜欢
  • 1970-01-01
  • 2023-02-22
  • 2018-07-22
  • 2020-12-26
  • 1970-01-01
  • 1970-01-01
  • 2018-07-16
  • 2020-01-21
  • 2021-06-25
相关资源
最近更新 更多