อาร์เรย์ไม่ได้รับการกำหนด - React Redux

ฉันยังใหม่กับ React และ Redux พยายามที่จะเข้าใจพื้นฐานและทำตัวอย่างง่ายๆ แต่ฉันติดอยู่ในปัญหานี้มากกว่าหนึ่งวัน ฉันไม่พบวิธีแก้ไข ฉันคิดว่าความผิดพลาดของฉันเป็นความผิดพลาดที่โง่เขลา

ปัญหาคือฉันไม่สามารถพิมพ์อาร์เรย์ของผู้ใช้ได้ เมื่อทำการดีบัก ตัวแปร ผู้ใช้ กำลังโหลดด้วยรหัสและผู้ใช้ที่ถูกต้องทั้งหมด แต่หลังจากดำเนินการ <li key={id}>{name}</li> สามครั้ง ตัวแปรจะกลับมาที่ forEach และให้ข้อยกเว้นนี้แก่ฉัน: TypeError ที่ไม่ถูกตรวจจับ: ไม่สามารถอ่านคุณสมบัติ 'forEach' ของไม่ได้กำหนด โดยที่ ผู้ใช้ ไม่ได้กำหนดไว้ และฉันยังได้รับข้อผิดพลาดที่สอดคล้องกับ PropTypes: ผู้ใช้ prop ไม่ถูกต้องของอาร์เรย์ประเภทที่ส่งไปยังโฮมเพจ วัตถุที่คาดหวัง

นี่คือรหัส:

ร้านค้า/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;

ตัวลด/index.js

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

export default combineReducers({
    user: userReducer
});

ตัวลด/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;
    }
}

การกระทำ/usersAction.js

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

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

ส่วนประกอบ/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);

person Bruno Paiva    schedule 21.07.2018    source แหล่งที่มา


คำตอบ (2)


คุณกำลังคืนรูปทรงที่ไม่ถูกต้องในตัวลดของคุณ รหัสที่เกี่ยวข้องของคุณ:

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

ที่นี่ state เป็นวัตถุ แต่คุณส่งคืนมันในอาร์เรย์โดยการกระจายมัน ดังนั้นสถานะของคุณจึงพังทลาย

ลองแบบนั้น:

case GET_USERS:
    return state;

ดังที่ @Idan Dagan ชี้ให้เห็นในคำตอบของเขา จริงๆ แล้วเราไม่ได้กลายพันธุ์สถานะในตัวลดของเรา ฉันเพิ่งให้คำแนะนำนี้เนื่องจากคุณแค่เล่นเพื่อเรียนรู้ Redux และเรากำลังคืนสถานะดั้งเดิมที่นี่ ไม่มีอะไรเพิ่มเติม แต่นี่เป็นวิธีที่เหมาะสมและดีกว่าในการคืนสถานะ:

case GET_USERS:
    return { ...state };

นี่คือรหัสที่ใช้งานได้: https://codesandbox.io/s/k5ymxwknpv

ฉันยังเปลี่ยน forEach ด้วย map อีกครั้งตามที่ @Idan Dagan แนะนำ ฉันไม่ได้ตระหนักถึงสิ่งนั้น 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 ตอนนี้สถานะของคุณกลายเป็นข้อมูลทดสอบนี้ จากนั้น GET_USERS ของคุณจะถูกโจมตีและคุณจะส่งคืนออบเจ็กต์ใหม่ซึ่งรวมสถานะซึ่งเป็น TEST นี่คือขั้นตอน:

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);

or

return {...initialState}

ตัวเลือกสุดท้ายช่วยให้คุณเข้าใจมากขึ้นว่าคำอธิบายแรกของฉันทำงานอย่างไร ลองคืนสิ่งนี้ให้กับ GET_USERS ของคุณ:

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

คุณจะเห็นรายชื่อผู้ใช้พร้อมข้อมูล TEST แต่รายการสุดท้ายจะเป็น foo ของคุณ สิ่งนี้จะอธิบายว่าคุณสูญเสีย initialState ของคุณอย่างไร หากคุณส่งคืนสิ่งใด ๆ นอกเหนือจาก state ในกรณีเริ่มต้นของคุณ

คำแนะนำสุดท้าย คุณสามารถแก้ไขข้อบกพร่องการพัฒนา Redux ได้ด้วยเครื่องมือ Redux Dev มันเป็นเครื่องมือที่ยอดเยี่ยมและทำได้มากกว่าการดีบัก คุณสามารถติดตามการดำเนินการทั้งหมดของคุณสำหรับ Redux ได้อย่างง่ายดาย

person devserkan    schedule 21.07.2018
comment
ใช่ การแก้ไขภายใน การส่งคืน นี้จะหยุดข้อผิดพลาด ตอนนี้ฉันได้รับผู้ใช้แล้ว แต่ฉันได้ทำการทดสอบและฉันได้รับผู้ใช้เนื่องจากตัวเลือก ค่าเริ่มต้น ภายใน สวิตช์ ฉันไม่ได้ส่งการกระทำ GET_USERS.. ฉันพยายามเปลี่ยน ComponentDidMount() เป็น ComponentWillMount() แต่ไม่ประสบความสำเร็จ - person Bruno Paiva; 21.07.2018
comment
@BrunoPaiva ComponentWillMount ถือเป็นรุ่นเก่าและคุณควรหลีกเลี่ยง - person Idan Dagan; 21.07.2018
comment
DidMount หรือ WillMount ไม่มีผลใด ๆ กับสิ่งนี้ และอย่างที่บอกไปแล้วว่า WillMount ยังคงเป็นมรดกอยู่ในขณะนี้ คุณจะติดตามการกระทำของคุณได้อย่างไร? แน่ใจเหรอว่าไม่โดน? เราขอดูไฟล์ types ของคุณได้ไหม นอกจากนี้ คุณสามารถใส่ทั้งแอปของคุณไปที่ codesandbox.io เพื่อให้เราตรวจสอบได้ - person devserkan; 21.07.2018
comment
ฉันได้แก้ไขคำตอบและเพิ่มตัวอย่างโค้ดแซนด์บ็อกซ์ที่ใช้งานได้ นอกจากนี้ ฉันยังได้เปลี่ยน forEach ด้วย map ตามคำแนะนำของ @IdanDagan จับดี! - person devserkan; 21.07.2018
comment
codesandbox.io/s/my6k6o037p นี่คือรหัส.. ฉันเปลี่ยนการส่งคืนของ ค่าเริ่มต้น ภายใน สวิตช์ และยืนยันว่าการดำเนินการ GET_USERS ไม่ได้ถูกเรียก.. และขอขอบคุณทั้งสองท่านสำหรับความช่วยเหลือ!! ชื่นชมจริงๆ! - person Bruno Paiva; 22.07.2018
comment
ฉันได้แก้ไขคำตอบแล้วและพยายามอธิบายว่า GET_USERS ของคุณถูกโจมตีอย่างไร - person devserkan; 22.07.2018
comment
@devserkan เป็นการดีกว่าที่จะดีบักการกระทำผ่าน Redux DevTool สำหรับ chrome ยังไงก็ตาม ฉันให้คุณ upvoting ให้คุณแล้ว :) - person Idan Dagan; 22.07.2018
comment
@IdanDagan ฉันเห็นด้วยอย่างยิ่งกับคุณ ฉันใช้ Redux Dev Tools ทุกครั้งที่ฉันใช้ Redux มันเป็นเครื่องมือที่ยอดเยี่ยม ที่นี่ ฉันแค่อยากจะแสดงวิธีง่ายๆ เนื่องจาก OP พยายามแก้ไขปัญหาในตัวลด อย่างไรก็ตาม ฉันได้แก้ไขคำตอบและพูดถึงจาก Redux Dev Tools แล้ว ขอบคุณ. - person devserkan; 22.07.2018
comment
ขอบคุณเพื่อนๆ สำหรับบทเรียนอันน่าทึ่งนี้ :) ฉันไม่รู้มาก่อนว่าการดำเนินการ INIT ถูกเรียกก่อน! ตอนนี้ redux ชัดเจนสำหรับฉันมากขึ้น .. ขอบคุณอีกครั้งและขอให้มีวันดีๆ - person Bruno Paiva; 22.07.2018

ฉันไม่แน่ใจว่าคุณกำลังพยายามทำอะไรกับรัฐใหม่

แต่มีการเปลี่ยนแปลงสองสามอย่างที่คุณต้องทำ:

  1. ใช้ map แทน forEach (เพราะคุณต้องการส่งคืนอาร์เรย์ใหม่)
  2. ที่ตัวลดคุณสามารถคืนสถานะดังนี้:

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

ตามที่กล่าวไว้ใน เอกสาร redux:

เราไม่เปลี่ยนสถานะ เราสร้างสำเนาด้วย Object.assign()

person Idan Dagan    schedule 21.07.2018
comment
ขอบคุณ @Idan สำหรับความช่วยเหลือ ตามที่ฉันแสดงความคิดเห็นข้างต้น ยังคงไม่เรียกใช้การดำเนินการ GET_USERS.. - person Bruno Paiva; 22.07.2018