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

lzfw57am  于 2024-01-08  发布在  React
关注(0)|答案(1)|浏览(223)

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

  1. import { useContext, useEffect, useState } from "react";
  2. import Member from "./Member"
  3. import Table from 'react-bootstrap/Table';
  4. import { MemberContext } from "../contexts/MemberContext";
  5. import { Modal , Button} from "react-bootstrap";
  6. import AddForm from "./AddForm";
  7. import { connect, useDispatch, useSelector } from "react-redux";
  8. import { memberList } from '../actions/auth';
  9. const MemberList = (props) => {
  10. const { members } = useContext(MemberContext)
  11. const [show, setShow] = useState(false);
  12. // const member = useSelector((state) => state.contr);
  13. const dispatch = useDispatch();
  14. useEffect(() => {
  15. props.fetchmembers();
  16. // memberList()
  17. handleClose();
  18. }, [])
  19. const handleShow = () => setShow(true);
  20. const handleClose = () => setShow(false);
  21. //test = Array.from(props.auth.data);
  22. return props.auth.loading?(
  23. <h2>Loading</h2>
  24. ): props.auth.error?
  25. (
  26. <h2>{props.auth.error}</h2>
  27. ):(
  28. <>
  29. <div className="table-title">
  30. <div className="row">
  31. <div class="col-sm-6">
  32. <h2>Manage <b>Members</b></h2>
  33. </div>
  34. <div className="col-sm-6">
  35. <Button onClick={handleShow} className="btn btn-success" data-toggle="modal"><i className="material-icons">&#xE147;</i> <span>Add New Member</span></Button>
  36. </div>
  37. </div>
  38. </div>
  39. <Table striped bordered hover>
  40. <thead>
  41. <tr>
  42. <th>MemberID</th>
  43. <th>First Name</th>
  44. <th>Last Name</th>
  45. <th>Address</th>
  46. <th>City</th>
  47. <th>State</th>
  48. <th>Zip Code</th>
  49. <th>Email</th>
  50. <th>Phone</th>
  51. <th>Actions</th>
  52. </tr>
  53. </thead>
  54. <tbody>
  55. {
  56. props.auth && props.auth.data &&
  57. props.auth.data.map(item => (
  58. <tr key={item.id}>
  59. <Member item={item} />
  60. </tr>
  61. ))
  62. }
  63. </tbody>
  64. </Table>
  65. <Modal show={show} onHide={handleClose}>
  66. <Modal.Header closeButton>
  67. <Modal.Title>
  68. Add Member
  69. </Modal.Title>
  70. </Modal.Header>
  71. <Modal.Body>
  72. <AddForm/>
  73. </Modal.Body>
  74. <Modal.Footer>
  75. <Button onClick={handleClose} variant="secondary">
  76. Close
  77. </Button>
  78. </Modal.Footer>
  79. </Modal>
  80. </>
  81. )
  82. }
  83. const mapStateToProps = (state) => ({
  84. auth: state.auth
  85. });
  86. const mapDispatchtoprops = (dispatch) => {
  87. return {
  88. fetchmembers: () => dispatch(memberList())
  89. }
  90. }
  91. //export default MemberList
  92. export default connect(mapStateToProps,mapDispatchtoprops)(MemberList)

字符串
Member.js

  1. import { useContext, useState, useEffect } from "react";
  2. import MemberContext from "../contexts/MemberContext";
  3. import { Modal , Button} from "react-bootstrap";
  4. import EditForm from "./EditForm";
  5. const Member = ({item}) => {
  6. const [show, setShow] = useState(false);
  7. const handleShow = () => setShow(true);
  8. const handleClose = () => setShow(false);
  9. return(
  10. <>
  11. <td>{item.memberID}</td>
  12. <td>{item.first_name}</td>
  13. <td>{item.last_name}</td>
  14. <td>{item.address}</td>
  15. <td>{item.city}</td>
  16. <td>{item.state}</td>
  17. <td>{item.zip_code}</td>
  18. <td>{item.email}</td>
  19. <td>{item.phone}</td>
  20. <td><button onClick={handleShow} href="#editEmployeeModal" className="edit" data-toggle="modal"><i className="material-icons" data-toggle="tooltip" title="Edit">&#xE254;</i></button>
  21. <a href="#deleteEmployeeModal" className="delete" data-toggle="modal"><i className="material-icons" data-toggle="tooltip" title="Delete">&#xE872;</i></a>
  22. </td>
  23. <Modal show={show} onHide={handleClose}>
  24. <Modal.Header closeButton>
  25. <Modal.Title>
  26. Edit Member
  27. </Modal.Title>
  28. </Modal.Header>
  29. <Modal.Body>
  30. <EditForm theMember={item} />
  31. </Modal.Body>
  32. <Modal.Footer>
  33. <Button variant="secondary" onClick={handleClose}>
  34. Close Button
  35. </Button>
  36. </Modal.Footer>
  37. </Modal>
  38. </>
  39. )
  40. }
  41. export default Member


AddForm.js

  1. import { useState, useContext } from "react";
  2. import { Form, Button } from "react-bootstrap"
  3. import { MemberContext } from "../contexts/MemberContext";
  4. import { connect } from 'react-redux';
  5. import { addNewMember } from '../actions/auth';
  6. const AddForm = ({ addNewMember }) => {
  7. const [formData, setFormData] = useState({
  8. memberID: '',
  9. first_name: '',
  10. last_name: '',
  11. address: '',
  12. city: '',
  13. state: '',
  14. zip_code: '',
  15. email: '',
  16. phone: ''
  17. });
  18. const { memberID, first_name,last_name,address,city,state,zip_code, email, phone } = formData;
  19. const onChange = e => setFormData({ ...formData, [e.target.name]: e.target.value });
  20. const onSubmit = e => {
  21. e.preventDefault();
  22. addNewMember(memberID, first_name, last_name, address, city, state, zip_code, email,phone);
  23. };
  24. return (
  25. <Form onSubmit={e => onSubmit(e)}>
  26. <Form.Group>
  27. <Form.Control
  28. type="text"
  29. placeholder="MemberID *"
  30. name="memberID"
  31. value={memberID}
  32. onChange={e => onChange(e)}
  33. required
  34. />
  35. </Form.Group>
  36. <br/>
  37. <Form.Group>
  38. <Form.Control
  39. type="text"
  40. placeholder="First Name *"
  41. name="first_name"
  42. value={first_name}
  43. onChange={e => onChange(e)}
  44. required
  45. />
  46. </Form.Group>
  47. <br/>
  48. <Form.Group>
  49. <Form.Control
  50. type="text"
  51. placeholder="Last Name *"
  52. name="last_name"
  53. value={last_name}
  54. onChange={e => onChange(e)}
  55. required
  56. />
  57. </Form.Group>
  58. <br/>
  59. <Form.Group>
  60. <Form.Control
  61. type="text"
  62. placeholder="Address *"
  63. name="address"
  64. value={address}
  65. onChange={e => onChange(e)}
  66. required
  67. />
  68. </Form.Group>
  69. <br/>
  70. <Form.Group>
  71. <Form.Control
  72. type="text"
  73. placeholder="City *"
  74. name="city"
  75. value={city}
  76. onChange={e => onChange(e)}
  77. required
  78. />
  79. </Form.Group>
  80. <br/>
  81. <Form.Group>
  82. <Form.Control
  83. type="text"
  84. placeholder="State *"
  85. name="state"
  86. value={state}
  87. onChange={e => onChange(e)}
  88. required
  89. />
  90. </Form.Group>
  91. <br/>
  92. <Form.Group>
  93. <Form.Control
  94. type="text"
  95. placeholder="Zip Code *"
  96. name="zip_code"
  97. value={zip_code}
  98. onChange={e => onChange(e)}
  99. required
  100. />
  101. </Form.Group>
  102. <br/>
  103. <Form.Group>
  104. <Form.Control
  105. type="email"
  106. placeholder="Email *"
  107. name="email"
  108. value={email}
  109. onChange={e => onChange(e)}
  110. required
  111. />
  112. </Form.Group>
  113. <br/>
  114. <Form.Group>
  115. <Form.Control
  116. type="text"
  117. placeholder="Phone *"
  118. name="phone"
  119. value={phone}
  120. onChange={e => onChange(e)}
  121. required
  122. />
  123. </Form.Group>
  124. <br/>
  125. <Button variant="success" type="submit" block>
  126. Add new Member
  127. </Button>
  128. </Form>
  129. )
  130. }
  131. //export default AddForm;
  132. export default connect(null, { addNewMember })(AddForm);


store.js

  1. import { applyMiddleware, compose, combineReducers } from 'redux';
  2. import { composeWithDevTools } from 'redux-devtools-extension';
  3. import thunk from 'redux-thunk';
  4. import rootReducer from './reducers';
  5. import auth from './reducers/auth';
  6. import contr from './reducers/contr';
  7. import { legacy_createStore as createStore } from 'redux';
  8. const composeEnhancers = (typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose
  9. const reducers = combineReducers({
  10. auth: auth,
  11. contr: contr,
  12. });
  13. const initialState = {};
  14. const middleware = [thunk];
  15. const store = createStore(
  16. reducers,
  17. composeEnhancers(applyMiddleware(thunk)),
  18. )
  19. export default store;


auth.js(reducer)

  1. import {
  2. LOGIN_SUCCESS,
  3. LOGIN_FAIL,
  4. USER_LOADED_SUCCESS,
  5. USER_LOADED_FAIL,
  6. AUTHENTICATED_SUCCESS,
  7. AUTHENTICATED_FAIL,
  8. PASSWORD_RESET_SUCCESS,
  9. PASSWORD_RESET_FAIL,
  10. PASSWORD_RESET_CONFIRM_SUCCESS,
  11. PASSWORD_RESET_CONFIRM_FAIL,
  12. SIGNUP_SUCCESS,
  13. SIGNUP_FAIL,
  14. ACTIVATION_SUCCESS,
  15. ACTIVATION_FAIL,
  16. MEMBER_LIST_SUCCESS,
  17. MEMBER_LIST_FAIL,
  18. MEMBER_LIST_REQUEST,
  19. MEMBER_ADD_SUCCESS,
  20. MEMBER_ADD_FAIL,
  21. LOGOUT
  22. } from '../actions/types';
  23. const initialState = {
  24. access: localStorage.getItem('access'),
  25. refresh: localStorage.getItem('refresh'),
  26. isAuthenticated: null,
  27. user: null,
  28. data: [],
  29. error: '',
  30. hasError: false,
  31. loading: true
  32. };
  33. export default function(state = initialState, action) {
  34. const { type, payload } = action;
  35. switch(type) {
  36. case AUTHENTICATED_SUCCESS:
  37. return {
  38. ...state,
  39. isAuthenticated: true
  40. }
  41. case LOGIN_SUCCESS:
  42. localStorage.setItem('access', payload.access);
  43. return {
  44. ...state,
  45. isAuthenticated: true,
  46. access: payload.access,
  47. refresh: payload.refresh,
  48. hasError: false,
  49. }
  50. case USER_LOADED_SUCCESS:
  51. return {
  52. ...state,
  53. user: payload,
  54. hasError: false,
  55. }
  56. case SIGNUP_SUCCESS:
  57. return {
  58. ...state,
  59. isAuthenticated: false,
  60. hasError: false ,
  61. }
  62. case MEMBER_LIST_SUCCESS:
  63. return{
  64. ...state,
  65. loading: false,
  66. data: action.payload,
  67. error: ''
  68. }
  69. case MEMBER_LIST_REQUEST:
  70. return{
  71. ...state,
  72. loading: true
  73. }
  74. case MEMBER_ADD_SUCCESS:
  75. return {
  76. ...state,
  77. loading: false,
  78. data: action.payload,
  79. error: ''
  80. }
  81. case MEMBER_LIST_FAIL:
  82. return {
  83. loading: false,
  84. data:[],
  85. error: action.payload
  86. }
  87. case USER_LOADED_FAIL:
  88. return {
  89. ...state,
  90. user: null
  91. }
  92. case AUTHENTICATED_FAIL:
  93. return {
  94. ...state,
  95. isAuthenticated: false
  96. }
  97. case SIGNUP_FAIL:
  98. return {
  99. ...state,
  100. hasError: true,
  101. }
  102. case LOGOUT:
  103. localStorage.removeItem('access');
  104. localStorage.removeItem('refresh');
  105. return {
  106. ...state,
  107. access: null,
  108. refresh: null,
  109. isAuthenticated: false,
  110. user: null
  111. }
  112. case LOGIN_FAIL:
  113. localStorage.removeItem('access');
  114. localStorage.removeItem('refresh');
  115. return {
  116. ...state,
  117. access: null,
  118. refresh: null,
  119. isAuthenticated: false,
  120. user: null,
  121. hasError: true,
  122. }
  123. case MEMBER_ADD_FAIL:
  124. return {
  125. loading: false,
  126. data:[],
  127. error: action.payload
  128. }
  129. case PASSWORD_RESET_SUCCESS:
  130. case PASSWORD_RESET_FAIL:
  131. case PASSWORD_RESET_CONFIRM_SUCCESS:
  132. case PASSWORD_RESET_CONFIRM_FAIL:
  133. case ACTIVATION_SUCCESS:
  134. case ACTIVATION_FAIL:
  135. return {
  136. ...state
  137. }
  138. default:
  139. return state
  140. }
  141. }


auth.js(actions)

  1. export const addNewMember = (memberID, first_name, last_name, address, city, state, zip_code, email, phone) => async dispatch => {
  2. const config = {
  3. headers: {
  4. 'Content-Type': 'application/json'
  5. }
  6. };
  7. const body = JSON.stringify({ memberID, first_name, last_name, address, city, state,zip_code,email,phone });
  8. try {
  9. const res = await axios.post(`${process.env.REACT_APP_API_URL}/api/member/`, body, config);
  10. dispatch({
  11. type: MEMBER_ADD_SUCCESS,
  12. payload: res.data
  13. });
  14. toast.success('Check your Email to verify Account');
  15. } catch (err) {
  16. dispatch({
  17. type: MEMBER_ADD_FAIL
  18. })
  19. }
  20. };
  21. const memberListRequest = () => {
  22. return {
  23. type: MEMBER_LIST_REQUEST
  24. }
  25. }
  26. const memberListSuccess = (data) => {
  27. return {
  28. type: MEMBER_LIST_SUCCESS,
  29. payload: data
  30. }
  31. }
  32. const memberListFailure = (err) => {
  33. return {
  34. type: MEMBER_LIST_FAIL,
  35. payload: err
  36. }
  37. }
  38. export const memberList = () => {
  39. return (dispatch) => {
  40. dispatch(memberListRequest);
  41. axios.get(`${process.env.REACT_APP_API_URL}/api/member/`).then(res=>{
  42. let _list=res.data
  43. dispatch(memberListSuccess(_list))
  44. }).catch(err=>{
  45. dispatch(memberListFailure(err.message))
  46. })
  47. }
  48. }


来自MEMBER_ADD_SUCCESS的有效负载

  1. type(pin): "MEMBER_ADD_SUCCESS"
  2. id(pin): 19
  3. memberID(pin): "18"
  4. first_name(pin): "John"
  5. last_name(pin): "Doe"
  6. address(pin): "123 Manchester"
  7. city(pin): "SI"
  8. state(pin): "NY"
  9. zip_code(pin): "10312"
  10. email(pin): "[email protected]"
  11. phone(pin): "1112345556"


MEMBER_LIST_SUCCESS的有效负载

  1. type(pin): "MEMBER_LIST_SUCCESS"
  2. id(pin): 1
  3. memberID(pin): "80"
  4. first_name(pin): "Jose"
  5. last_name(pin): "Padilla"
  6. address(pin): "29 Gibson Dr"
  7. city(pin): "Hazlet"
  8. state(pin): "NJ"
  9. zip_code(pin): "07730"
  10. email(pin): "[email protected]"
  11. phone(pin): "917-324-5844"
  12. id(pin): 2
  13. memberID(pin): "1"
  14. first_name(pin): "Pedro"
  15. last_name(pin): "Padilla"
  16. address(pin): "35 Gibson Dr"
  17. city(pin): "Hazlet"
  18. state(pin): "NJ"
  19. zip_code(pin): "07730"
  20. email(pin): "[email protected]"
  21. phone(pin): "917-324-5845"


有效负载MEMBER_ADD_SUCCESS

  1. {"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"}


响应

  1. {
  2. "id": 20,
  3. "memberID": "19",
  4. "first_name": "Jane",
  5. "last_name": "Doe",
  6. "address": "1 Liberty St",
  7. "city": "Red Bank",
  8. "state": "NJ",
  9. "zip_code": "07748",
  10. "email": "[email protected]",
  11. "phone": "980-789-0987"
  12. }


会员列表_成功案例
预览

  1. [{id: 1, memberID: "80", first_name: "Jose", last_name: "Padilla", address: "29 Gibson Dr",…},…]
  2. 0
  3. :
  4. {id: 1, memberID: "80", first_name: "Jose", last_name: "Padilla", address: "29 Gibson Dr",…}
  5. address
  6. :
  7. "29 Gibson Dr"
  8. city
  9. :
  10. "Hazlet"
  11. email
  12. :
  13. "[email protected]"
  14. first_name
  15. :
  16. "Jose"
  17. id
  18. :
  19. 1
  20. last_name
  21. :
  22. "Padilla"
  23. memberID
  24. :
  25. "80"
  26. phone
  27. :
  28. "917-324-5844"
  29. state
  30. :
  31. "NJ"
  32. zip_code
  33. :
  34. "07730"
  35. 1
  36. :
  37. {id: 2, memberID: "1", first_name: "Pedro", last_name: "Padilla", address: "35 Gibson Dr",…}
  38. 2
  39. :
  40. {id: 3, memberID: "34", first_name: "Sandro", last_name: "Escobar", address: "1 Portacarrero Pl",…}
  41. 3
  42. :
  43. {id: 4, memberID: "16", first_name: "Luke", last_name: "Padilla", address: "56 Gibson Dr",…}


响应

  1. [
  2. {
  3. "id": 1,
  4. "memberID": "80",
  5. "first_name": "Jose",
  6. "last_name": "Padilla",
  7. "address": "29 Gibson Dr",
  8. "city": "Hazlet",
  9. "state": "NJ",
  10. "zip_code": "07730",
  11. "email": "[email protected]",
  12. "phone": "917-324-5844"
  13. },
  14. {
  15. "id": 2,
  16. "memberID": "1",
  17. "first_name": "Pedro",
  18. "last_name": "Padilla",
  19. "address": "35 Gibson Dr",
  20. "city": "Hazlet",
  21. "state": "NJ",
  22. "zip_code": "07730",
  23. "email": "[email protected]",
  24. "phone": "917-324-5845"
  25. }
  26. ]

hmtdttj4

hmtdttj41#

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

  1. [
  2. {
  3. "id": 1,
  4. "memberID": "80",
  5. "first_name": "Jose",
  6. "last_name": "Padilla",
  7. "address": "29 Gibson Dr",
  8. "city": "Hazlet",
  9. "state": "NJ",
  10. "zip_code": "07730",
  11. "email": "[email protected]",
  12. "phone": "917-324-5844"
  13. },
  14. {
  15. "id": 2,
  16. "memberID": "1",
  17. "first_name": "Pedro",
  18. "last_name": "Padilla",
  19. "address": "35 Gibson Dr",
  20. "city": "Hazlet",
  21. "state": "NJ",
  22. "zip_code": "07730",
  23. "email": "[email protected]",
  24. "phone": "917-324-5845"
  25. }
  26. ]

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

  1. {
  2. "id": 20,
  3. "memberID": "19",
  4. "first_name": "Jane",
  5. "last_name": "Doe",
  6. "address": "1 Liberty St",
  7. "city": "Red Bank",
  8. "state": "NJ",
  9. "zip_code": "07748",
  10. "email": "[email protected]",
  11. "phone": "980-789-0987"
  12. }


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

  1. export default function(state = initialState, action) {
  2. const { type, payload } = action;
  3. switch(type) {
  4. ...
  5. case MEMBER_LIST_SUCCESS:
  6. return {
  7. ...state,
  8. loading: false,
  9. data: payload, // <-- entire array
  10. error: ''
  11. }
  12. ...
  13. case MEMBER_ADD_SUCCESS:
  14. return {
  15. ...state,
  16. loading: false,
  17. // append and return new array
  18. data: state.data.concat(payload), // <-- maintain array invariant
  19. // or data: [...state.data, payload],
  20. error: ''
  21. }
  22. ...
  23. default:
  24. return state;
  25. }
  26. };

展开查看全部

相关问题