redux React Getting error props.auth.data.map not a function after use modal to create a record.记录已添加,但我需要重新加载页面才能看到结果

lzfw57am  于 12个月前  发布在  React
关注(0)|答案(1)|浏览(186)

父组件是一个表,它显示了表中成员的所有记录。我从django的API中获取数据。我有一个允许我创建新成员的模式。我可以创建新成员,但它崩溃了,并给予我一个错误,props.auth.data.map不是一个函数。如果我重新加载页面,它会向我显示表中的新记录。
MemberList.js

import { useContext, useEffect, useState } from "react";
import Member from "./Member"
import Table from 'react-bootstrap/Table';
import { MemberContext } from "../contexts/MemberContext";
import { Modal , Button} from "react-bootstrap";
import AddForm from "./AddForm";
import { connect, useDispatch, useSelector } from "react-redux";
import { memberList } from '../actions/auth';

const MemberList = (props) => {
    const { members } = useContext(MemberContext)
    const [show, setShow] = useState(false);
    // const member = useSelector((state) => state.contr);
    const dispatch = useDispatch();

    useEffect(() => {
      props.fetchmembers();
      // memberList()
      handleClose();
    }, [])

    const handleShow = () => setShow(true);
    const handleClose = () => setShow(false);
    //test = Array.from(props.auth.data);

    return props.auth.loading?(
        <h2>Loading</h2>
    ): props.auth.error? 
    (
        <h2>{props.auth.error}</h2>
    ):(
        <>
            <div className="table-title">
                <div className="row">
                    <div class="col-sm-6">
                        <h2>Manage <b>Members</b></h2>
                    </div>
                    <div className="col-sm-6">
                    <Button onClick={handleShow} className="btn btn-success" data-toggle="modal"><i className="material-icons">&#xE147;</i> <span>Add New Member</span></Button>                    
                    </div>
                </div>
            </div>
            <Table striped bordered hover>
                <thead>
                    <tr>
                        <th>MemberID</th>
                        <th>First Name</th>
                        <th>Last Name</th>
                        <th>Address</th>
                        <th>City</th>
                        <th>State</th>
                        <th>Zip Code</th>
                        <th>Email</th>
                        <th>Phone</th>
                        <th>Actions</th>
                    </tr>
                </thead>
                <tbody>
                    {
                        props.auth && props.auth.data &&
                      props.auth.data.map(item => (
                            <tr key={item.id}>
                                <Member item={item} />
                            </tr>
                        ))
                    }

                </tbody>
            </Table>

            <Modal show={show}  onHide={handleClose}>
                <Modal.Header closeButton>
                    <Modal.Title>
                        Add Member
                    </Modal.Title>
                </Modal.Header>
                <Modal.Body>
                    <AddForm/>
                </Modal.Body>
                <Modal.Footer>
                    <Button onClick={handleClose} variant="secondary">
                        Close
                    </Button>
                </Modal.Footer>
            </Modal>
        </>
    )
}

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

const mapDispatchtoprops = (dispatch) => {
    return {
        fetchmembers: () => dispatch(memberList())
    }
}

//export default MemberList
export default connect(mapStateToProps,mapDispatchtoprops)(MemberList)

字符串
Member.js

import { useContext, useState, useEffect } from "react";
import MemberContext from "../contexts/MemberContext";
import { Modal , Button} from "react-bootstrap";
import EditForm from "./EditForm";

const Member = ({item}) => {
    const [show, setShow] = useState(false);
    const handleShow = () => setShow(true);
    const handleClose = () => setShow(false);
    
    return(
        <>
            <td>{item.memberID}</td>
            <td>{item.first_name}</td>
            <td>{item.last_name}</td>
            <td>{item.address}</td>
            <td>{item.city}</td>
            <td>{item.state}</td>
            <td>{item.zip_code}</td>
            <td>{item.email}</td>
            <td>{item.phone}</td>
            <td><button onClick={handleShow} href="#editEmployeeModal" className="edit" data-toggle="modal"><i className="material-icons" data-toggle="tooltip" title="Edit">&#xE254;</i></button>
                <a href="#deleteEmployeeModal" className="delete" data-toggle="modal"><i className="material-icons" data-toggle="tooltip" title="Delete">&#xE872;</i></a>
            </td>
            <Modal show={show} onHide={handleClose}>
        <Modal.Header closeButton>
            <Modal.Title>
                Edit Member
            </Modal.Title>
        </Modal.Header>
        <Modal.Body>
            <EditForm theMember={item} />
        </Modal.Body>
        <Modal.Footer>
                <Button variant="secondary" onClick={handleClose}>
                    Close Button
                </Button>
        </Modal.Footer>
    </Modal>
        </>
    )
}

export default Member


AddForm.js

import { useState, useContext } from "react";
import { Form, Button } from "react-bootstrap"
import { MemberContext } from "../contexts/MemberContext";
import { connect } from 'react-redux';
import { addNewMember } from '../actions/auth';

const AddForm = ({ addNewMember }) => {
  const [formData, setFormData] = useState({
    memberID: '',
    first_name: '',
    last_name: '',
    address: '',
    city: '',
    state: '',
    zip_code: '',
    email: '',
    phone: ''
  });
  const { memberID, first_name,last_name,address,city,state,zip_code, email, phone } = formData;

  const onChange = e => setFormData({ ...formData, [e.target.name]: e.target.value });

  const onSubmit = e => {
    e.preventDefault();
       addNewMember(memberID, first_name, last_name, address, city, state, zip_code, email,phone);
  };

  return (
    <Form onSubmit={e => onSubmit(e)}>
      <Form.Group>
        <Form.Control
          type="text"
          placeholder="MemberID *"
          name="memberID"
          value={memberID}
          onChange={e => onChange(e)}
          required
        />
      </Form.Group>
      <br/>
      <Form.Group>
        <Form.Control
          type="text"
          placeholder="First Name *"
          name="first_name"
          value={first_name}
          onChange={e => onChange(e)}
          required
        />
      </Form.Group>
      <br/>
      <Form.Group>
        <Form.Control
          type="text"
          placeholder="Last Name *"
          name="last_name"
          value={last_name}
          onChange={e => onChange(e)}
          required
        />
      </Form.Group>
      <br/>
      <Form.Group>
        <Form.Control
          type="text"
          placeholder="Address *"
          name="address"
          value={address}
          onChange={e => onChange(e)}
          required
        />
      </Form.Group>
      <br/>
      <Form.Group>
        <Form.Control
          type="text"
          placeholder="City *"
          name="city"
          value={city}
          onChange={e => onChange(e)}
          required
        />
      </Form.Group>
      <br/>
      <Form.Group>
        <Form.Control
          type="text"
          placeholder="State *"
          name="state"
          value={state}
          onChange={e => onChange(e)}
          required
        />
      </Form.Group>
      <br/>
      <Form.Group>
        <Form.Control
          type="text"
          placeholder="Zip Code *"
          name="zip_code"
          value={zip_code}
          onChange={e => onChange(e)}
          required
        />
      </Form.Group>
      <br/>
      <Form.Group>
        <Form.Control
          type="email"
          placeholder="Email *"
          name="email"
          value={email}
          onChange={e => onChange(e)}
          required
        />
      </Form.Group>
      <br/>
      <Form.Group>
        <Form.Control
          type="text"
          placeholder="Phone *"
          name="phone"
          value={phone}
          onChange={e => onChange(e)}
          required
        />
      </Form.Group>
      <br/>
      <Button variant="success" type="submit" block> 
        Add new Member
      </Button>
    </Form>
  )
}

//export default AddForm;
export default connect(null, { addNewMember })(AddForm);


store.js

import { applyMiddleware, compose, combineReducers } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
import auth from './reducers/auth';
import contr from './reducers/contr';
import { legacy_createStore as createStore } from 'redux';

const composeEnhancers = (typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose

const reducers = combineReducers({
    auth: auth, 
    contr: contr, 
});

const initialState = {};

const middleware = [thunk];

const store = createStore(
    reducers, 
    composeEnhancers(applyMiddleware(thunk)),
)

export default store;


auth.js(reducer)

import {
    LOGIN_SUCCESS,
    LOGIN_FAIL,
    USER_LOADED_SUCCESS,
    USER_LOADED_FAIL,
    AUTHENTICATED_SUCCESS,
    AUTHENTICATED_FAIL,
    PASSWORD_RESET_SUCCESS,
    PASSWORD_RESET_FAIL,
    PASSWORD_RESET_CONFIRM_SUCCESS,
    PASSWORD_RESET_CONFIRM_FAIL,
    SIGNUP_SUCCESS,
    SIGNUP_FAIL,
    ACTIVATION_SUCCESS,
    ACTIVATION_FAIL,
    MEMBER_LIST_SUCCESS,
    MEMBER_LIST_FAIL,
    MEMBER_LIST_REQUEST,
    MEMBER_ADD_SUCCESS,
    MEMBER_ADD_FAIL,
    LOGOUT
} from '../actions/types';

const initialState = {
    access: localStorage.getItem('access'),
    refresh: localStorage.getItem('refresh'),
    isAuthenticated: null,
    user: null,
    data: [],
    error: '',
    hasError: false,
    loading: true
};

export default function(state = initialState, action) {
    const { type, payload } = action;

    switch(type) {
        case AUTHENTICATED_SUCCESS:
            return {
                ...state,
                isAuthenticated: true
            }
        case LOGIN_SUCCESS:
            localStorage.setItem('access', payload.access);
            return {
                ...state,
                isAuthenticated: true,
                access: payload.access,
                refresh: payload.refresh,
                hasError: false,
                
            }
        case USER_LOADED_SUCCESS:
            return {
                ...state,
                user: payload,
                hasError: false,
                
            }
        case SIGNUP_SUCCESS:
            return {
                ...state,
                isAuthenticated: false,
                hasError: false ,
                
                }
        case MEMBER_LIST_SUCCESS:
            return{
                ...state,
                        
                loading: false,
                data: action.payload,
                error: ''
                    }
        case MEMBER_LIST_REQUEST:
            return{
                ...state,
                loading: true
            }
            case MEMBER_ADD_SUCCESS:
                return {
                    ...state,
                    
                    loading: false,
                    data: action.payload,
                    error: ''
                }
        case MEMBER_LIST_FAIL:
            return {
                loading: false,
                data:[],
                error: action.payload
                        }
        case USER_LOADED_FAIL:
            return {
                ...state,
                user: null
            }
        case AUTHENTICATED_FAIL:
            return {
                ...state,
                isAuthenticated: false
                }
        case SIGNUP_FAIL:
            return {
                ...state,
                hasError: true,
                

            }
        case LOGOUT:
                localStorage.removeItem('access');
                localStorage.removeItem('refresh');
            return {
                ...state,
                access: null,
                refresh: null,
                isAuthenticated: false,
                user: null
                    }
        case LOGIN_FAIL:
                localStorage.removeItem('access');
                localStorage.removeItem('refresh');
            return {
                    ...state,
                    access: null,
                    refresh: null,
                    isAuthenticated: false,
                    user: null,
                    hasError: true,
                    
                }
        case MEMBER_ADD_FAIL:
            return {
                loading: false,
                data:[],
                error: action.payload
            }
        case PASSWORD_RESET_SUCCESS:
        case PASSWORD_RESET_FAIL:
        case PASSWORD_RESET_CONFIRM_SUCCESS:
        case PASSWORD_RESET_CONFIRM_FAIL:
        case ACTIVATION_SUCCESS:
        case ACTIVATION_FAIL:
            return {
                ...state
            }
        default:
                return state
    }
}


auth.js(actions)

export const addNewMember = (memberID, first_name, last_name, address, city, state, zip_code, email, phone) => async dispatch => {
    const config = {
        headers: {
            'Content-Type': 'application/json'
        }
    };

    const body = JSON.stringify({ memberID, first_name, last_name, address, city, state,zip_code,email,phone });

    try {
        const res = await axios.post(`${process.env.REACT_APP_API_URL}/api/member/`, body, config);

        dispatch({
            type: MEMBER_ADD_SUCCESS,
            payload: res.data
        });
        toast.success('Check your Email to verify Account');
    } catch (err) {
        dispatch({
            type: MEMBER_ADD_FAIL
        })
    }
};

const memberListRequest = () => {
    return {
        type: MEMBER_LIST_REQUEST
    }
}

const memberListSuccess = (data) => {
    return {
        type: MEMBER_LIST_SUCCESS,
        payload: data
    }
}

const memberListFailure = (err) => {
    return {
        type: MEMBER_LIST_FAIL,
        payload: err
    }
}

export const memberList = () => {
    return (dispatch) => {
        dispatch(memberListRequest);
        axios.get(`${process.env.REACT_APP_API_URL}/api/member/`).then(res=>{
            let _list=res.data
            dispatch(memberListSuccess(_list))
        }).catch(err=>{
            dispatch(memberListFailure(err.message))
        })
    }
}


来自MEMBER_ADD_SUCCESS的有效负载

type(pin): "MEMBER_ADD_SUCCESS"
id(pin): 19
memberID(pin): "18"
first_name(pin): "John"
last_name(pin): "Doe"
address(pin): "123 Manchester"
city(pin): "SI"
state(pin): "NY"
zip_code(pin): "10312"
email(pin): "[email protected]"
phone(pin): "1112345556"


MEMBER_LIST_SUCCESS的有效负载

type(pin): "MEMBER_LIST_SUCCESS"
id(pin): 1
memberID(pin): "80"
first_name(pin): "Jose"
last_name(pin): "Padilla"
address(pin): "29 Gibson Dr"
city(pin): "Hazlet"
state(pin): "NJ"
zip_code(pin): "07730"
email(pin): "[email protected]"
phone(pin): "917-324-5844"
id(pin): 2
memberID(pin): "1"
first_name(pin): "Pedro"
last_name(pin): "Padilla"
address(pin): "35 Gibson Dr"
city(pin): "Hazlet"
state(pin): "NJ"
zip_code(pin): "07730"
email(pin): "[email protected]"
phone(pin): "917-324-5845"


有效负载MEMBER_ADD_SUCCESS

{"memberID":"19","first_name":"Jane","last_name":"Doe","address":"1 Liberty St","city":"Red Bank","state":"NJ","zip_code":"07748","email":"[email protected]","phone":"980-789-0987"}


响应

{
    "id": 20,
    "memberID": "19",
    "first_name": "Jane",
    "last_name": "Doe",
    "address": "1 Liberty St",
    "city": "Red Bank",
    "state": "NJ",
    "zip_code": "07748",
    "email": "[email protected]",
    "phone": "980-789-0987"
}


会员列表_成功案例
预览

[{id: 1, memberID: "80", first_name: "Jose", last_name: "Padilla", address: "29 Gibson Dr",…},…]
0
: 
{id: 1, memberID: "80", first_name: "Jose", last_name: "Padilla", address: "29 Gibson Dr",…}
address
: 
"29 Gibson Dr"
city
: 
"Hazlet"
email
: 
"[email protected]"
first_name
: 
"Jose"
id
: 
1
last_name
: 
"Padilla"
memberID
: 
"80"
phone
: 
"917-324-5844"
state
: 
"NJ"
zip_code
: 
"07730"
1
: 
{id: 2, memberID: "1", first_name: "Pedro", last_name: "Padilla", address: "35 Gibson Dr",…}
2
: 
{id: 3, memberID: "34", first_name: "Sandro", last_name: "Escobar", address: "1 Portacarrero Pl",…}
3
: 
{id: 4, memberID: "16", first_name: "Luke", last_name: "Padilla", address: "56 Gibson Dr",…}


响应

[
    {
        "id": 1,
        "memberID": "80",
        "first_name": "Jose",
        "last_name": "Padilla",
        "address": "29 Gibson Dr",
        "city": "Hazlet",
        "state": "NJ",
        "zip_code": "07730",
        "email": "[email protected]",
        "phone": "917-324-5844"
    },
    {
        "id": 2,
        "memberID": "1",
        "first_name": "Pedro",
        "last_name": "Padilla",
        "address": "35 Gibson Dr",
        "city": "Hazlet",
        "state": "NJ",
        "zip_code": "07730",
        "email": "[email protected]",
        "phone": "917-324-5845"
    }
]

hmtdttj4

hmtdttj41#

您正在使用的两个API终结点之间的响应值不匹配。当您重新加载页面时,将发出GET请求并使用作为数组的响应值,但当添加成员时,POST响应值是对象并替换状态中的数组值。
MEMBER_LIST_SUCCESS操作传递了一个对象数组:

[
  {
    "id": 1,
    "memberID": "80",
    "first_name": "Jose",
    "last_name": "Padilla",
    "address": "29 Gibson Dr",
    "city": "Hazlet",
    "state": "NJ",
    "zip_code": "07730",
    "email": "[email protected]",
    "phone": "917-324-5844"
  },
  {
    "id": 2,
    "memberID": "1",
    "first_name": "Pedro",
    "last_name": "Padilla",
    "address": "35 Gibson Dr",
    "city": "Hazlet",
    "state": "NJ",
    "zip_code": "07730",
    "email": "[email protected]",
    "phone": "917-324-5845"
   }
]

字符串
MEMBER_ADD_SUCCESS动作传递一个对象时:

{
  "id": 20,
  "memberID": "19",
  "first_name": "Jane",
  "last_name": "Doe",
  "address": "1 Liberty St",
  "city": "Red Bank",
  "state": "NJ",
  "zip_code": "07748",
  "email": "[email protected]",
  "phone": "980-789-0987"
}


MEMBER_ADD_SUCCESS似乎只是添加的成员对象,而不是包含它的整个成员数组。更新MEMBER_ADD_SUCCESS reducer case,将此值附加到当前state.auth.data数组。
范例:

export default function(state = initialState, action) {
  const { type, payload } = action;

  switch(type) {
    ...

    case MEMBER_LIST_SUCCESS:
      return {
        ...state,
        loading: false,
        data: payload, // <-- entire array
        error: ''
      }

    ...

    case MEMBER_ADD_SUCCESS:
      return {
        ...state,
        loading: false,
        // append and return new array
        data: state.data.concat(payload), // <-- maintain array invariant
        // or data: [...state.data, payload],
        error: ''
      }

     ...

     default:
       return state;
  }
};

相关问题