Question:
Fix React error ‘props.auth.data.map’

Query: (React Getting error props.auth.data.map not a function after use modal to create a record. R…)


Problem

The parent component is a table that shows all the records I have for members in a table. I fetch that data from an API in Django. I have a modal that allows me to create a new member. I can create the new member but then it crashes and gives me an error that props.auth.data.map is not a function. If I reload the page then it will show me the new record in the table.


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

        })

    }

}


Payload from 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): "John@gmail.com"

phone(pin): "1112345556"

Payload for 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): "quito1970@gmail.com"

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): "quito1971@gmail.com"

phone(pin): "917-324-5845"


Payload MEMBER_ADD_SUCCESS


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


Response


{

    "id": 20,

    "memberID": "19",

    "first_name": "Jane",

    "last_name": "Doe",

    "address": "1 Liberty St",

    "city": "Red Bank",

    "state": "NJ",

    "zip_code": "07748",

    "email": "Jane@bny.com",

    "phone": "980-789-0987"

}


MEMBER_LIST_SUCCESS


Preview


[{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

"quito1970@gmail.com"

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",…}


Response


[

    {

        "id": 1,

        "memberID": "80",

        "first_name": "Jose",

        "last_name": "Padilla",

        "address": "29 Gibson Dr",

        "city": "Hazlet",

        "state": "NJ",

        "zip_code": "07730",

        "email": "quito1970@gmail.com",

        "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": "quito1971@gmail.com",

        "phone": "917-324-5845"

    }

]


Solution

There is a mismatch of the response value between two of the API endpoints you are using. When you reload the page the GET request is made and the response value that is an array is used, but when adding a member, the POST response value is an object and replaces the array value that was in state.


The MEMBER_LIST_SUCCESS action is passed an array of objects:


[

  {

    "id": 1,

    "memberID": "80",

    "first_name": "Jose",

    "last_name": "Padilla",

    "address": "29 Gibson Dr",

    "city": "Hazlet",

    "state": "NJ",

    "zip_code": "07730",

    "email": "quito1970@gmail.com",

    "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": "quito1971@gmail.com",

    "phone": "917-324-5845"

   }

]


While the MEMBER_ADD_SUCCESS action is passed an object:


{

  "id": 20,

  "memberID": "19",

  "first_name": "Jane",

  "last_name": "Doe",

  "address": "1 Liberty St",

  "city": "Red Bank",

  "state": "NJ",

  "zip_code": "07748",

  "email": "Jane@bny.com",

  "phone": "980-789-0987"

}


MEMBER_ADD_SUCCESS appears to just be the member object that was added instead of the entire array of members with it included. Update the MEMBER_ADD_SUCCESS reducer case to append this value to the current state.auth.data array.


Example:


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;

  }

};


Suggested blogs:

>How to conditionally render different arrays in React?

>Fix react useState and array.map not work issue

>Making infinite scrolling using react-query

>How does React State with JSON arrays affect performance?

>setlist{nosep} not work in the acronym package

>Fix ScrollIntoView not working issue

>Testing react component with jest

>How you can Show or hide elements in React?

>Testing react components using Hooks and Mocks

>Tips for using React Redux Hooks to show or hide React components


Nisha Patel

Nisha Patel

Submit
0 Answers