数组未定义-React Redux

时间:2018-07-21 19:11:07

标签: javascript reactjs redux react-redux

我是React和Redux的新手。试图理解基础知识并做一些简单的例子,但是我在这个问题上停留了超过一天,我找不到解决方法。我想我的错误是愚蠢的错误。

问题是我无法打印用户数组。调试时,变量 users 会加载所有正确的ID和用户,但是在执行<li key={id}>{name}</li> 3次之后,它返回到forEach并给出以下异常: 未捕获的TypeError:无法读取未定义 users 的属性'forEach' 。而且我还收到与PropTypes相对应的错误: 提供给HomePage的类型数组无效的prop用户,预期对象

代码如下:

store / configureStore.js

import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from '../reducers/index';

const initialState = {};

const middleware = [thunk];

const store = createStore(rootReducer, initialState, compose(
    applyMiddleware(...middleware),
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
));

export default store;

reducers / index.js

import { combineReducers } from 'redux';
import userReducer from './userReducer';
//import groupReducer from './groupReducer';

export default combineReducers({
    user: userReducer
});

reducers / userReducer.js

import { GET_USERS, ADD_USER, DELETE_USER } from '../actions/types';

const initialState = {
    users: [
        { id: 1, name: 'brunao'},
        { id: 2, name: 'flavio'},
        { id: 3, name: 'dudu'}
    ]
};

export default function(state = initialState, action) { 
    switch (action.type) {
        case GET_USERS:
            return [
                ...state
            ];
        default:
            return state;
    }
}

actions / usersAction.js

import { GET_USERS, ADD_USER, DELETE_USER } from './types';

export const getUsers = () => {
    return {
        type: GET_USERS
    };
};

components / HomePage.js

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getUsers } from '../actions/usersActions';
import PropTypes from 'prop-types';

class HomePage extends Component {

    componentDidMount() {
        this.props.getUsers();
    }

    render() {
        const { users } = this.props.user;
        return(
            <div>
                <h3>Users</h3>
                <ul>
                    {users.forEach(({id, name}) => (
                        <li key={id}>{name}</li>
                    ))}
                </ul>
            </div>
        );
    }

}

HomePage.propTypes = {
    getUsers: PropTypes.func.isRequired,
    user: PropTypes.object.isRequired
}

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

export default connect(mapStateToProps, { getUsers })(HomePage);

2 个答案:

答案 0 :(得分:2)

您在减速器中返回了错误的状态。您的相关代码:

export default function(state = initialState, action) { 
    switch (action.type) {
        case GET_USERS:
            return [
                ...state
            ];
        default:
            return state;
    }
}

在这里,状态是一个对象,但是您正在通过传播它将其以数组形式返回。因此,您的状态将被破坏。

尝试一下:

case GET_USERS:
    return state;

正如@Idan Dagan在他的回答中指出的那样,实际上,我们不会在化简器中改变状态。我刚刚提出了这个建议,因为您只是在玩耍学习Redux,我们将在这里返回原始状态,仅此而已。但是,这是一种返回状态的合适且更好的方法:

case GET_USERS:
    return { ...state };

以下是有效的代码:https://codesandbox.io/s/k5ymxwknpv

我还按照@Idan Dagan的建议,再次用forEach更改了map。我还没意识到。 forEach在这里不合适,因为实际上它不返回任何内容。您想在React中映射数组并渲染它们。

此外,您的州名令人困惑:) user.users有点奇怪,您可能会想一个更好的州。

评论后编辑

您的GET_USERS操作实际上已被击中,但是您在代码中检查了它是否错误。您正在做

export default function(state = initialState, action) {
  switch (action.type) {
    case GET_USERS:
      return Object.assign({}, state);
    default:
      return {
        users: [
          { id: 1, name: "TEST" },
          { id: 2, name: "TEST1" },
          { id: 3, name: "TEST2" }
        ]
      };
  }
}

这里发生了什么? Redux的第一个动作是INIT。这是您状态的初始化。现在,由于没有任何动作,减速器将达到默认情况并返回TEST。现在,您的状态成为该TEST数据。然后您的GET_USERS被击中,并且您返回一个新对象,该对象合并了处于测试状态的状态。步骤如下:

First, state is the `initialState` -> `INIT` runs and hits the default case
State is now TEST one -> GET_USERS hit and returns the `state` which is TEST one
You see the TEST one.

您如何测试自己的行为?只需将console.log放入减速器中即可:

export default function(state = initialState, action) {
  console.log("state",state);
  console.log("action",action);
  .....

,然后看到GET_USERS实际上被命中了。另一种选择是,不使用state返回合并的对象,而是尝试使用initialState合并它,或者使用传播算子通过使用initialState返回一个新对象:

return return Object.assign({}, initialState);

return {...initialState}

最后一个选项使您更加了解我的第一个解释的工作原理。尝试将其返回给您的GET_USERS:

return {...state, users:[...state.users, {id:4, name: "foo"}]};

您将看到一个包含TEST数据的用户列表,但最后一个将是您的foo。这说明了在默认情况下如果返回initialState旁的任何内容,将如何释放state

最后的建议是,您可以使用Redux Dev Tools调试Redux开发。它是一个很棒的工具,除了调试以外,还可以做更多的事情。您可以轻松跟踪Redux的所有操作。

答案 1 :(得分:0)

我不确定您打算如何处理新状态。

但是您需要做一些更改:

  1. 使用 map 代替 forEach (因为您要返回新数组)。
  2. 在减速器上,您可以像这样返回状态:

export default function(state = initialState, action) { 
    switch (action.type) {
        case GET_USERS:
            return Object.assign({}, state);
        default:
            return state;
    }
}

redux docs中所述:

我们不改变状态。我们使用Object.assign()创建一个副本。