父组件是一个表,它显示了表中成员的所有记录。我从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"></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"></i></button>
<a href="#deleteEmployeeModal" className="delete" data-toggle="modal"><i className="material-icons" data-toggle="tooltip" title="Delete"></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"
}
]
型
1条答案
按热度按时间hmtdttj41#
您正在使用的两个API终结点之间的响应值不匹配。当您重新加载页面时,将发出GET请求并使用作为数组的响应值,但当添加成员时,POST响应值是对象并替换状态中的数组值。
MEMBER_LIST_SUCCESS
操作传递了一个对象数组:字符串
当
MEMBER_ADD_SUCCESS
动作传递一个对象时:型
MEMBER_ADD_SUCCESS
似乎只是添加的成员对象,而不是包含它的整个成员数组。更新MEMBER_ADD_SUCCESS
reducer case,将此值附加到当前state.auth.data
数组。范例:
型