undefined object for map

I am learning react. I just want to test double binding for an array with hooks, so I create a table with and input that modifies the property of the object that I set in the state. After setting the state, I am getting a Cannot read property 'map' of undefined.

I compare original object and the new object and both have the same structure

//For App.js
import React, { useState } from 'react';
import Person from './Person/Person'
import './App.css';

const App = props => {

  const [peopleState, setPeopleState] = useState({
    people: [{ id: "001", name: "John Smith", address: "Main AV", phone: "312312" },
    { id: "002", name: "Marc Muller", address: "Second AV", phone: "345435" },
    { id: "003", name: "Sam Lopez", address: "Thrid AV", phone: "456444" },
    { id: "004", name: "Peter McNiece", address: "Road AV", phone: "213456" }]
  });

  const changeHandler = (event, index) => {
    //This should be changed by using a copy
    console.log(JSON.stringify(peopleState))
    const newList = peopleState;
    const newObj = peopleState.people[index];
    newObj.name = event.target.value;
    newList.people[index] = newObj;
    setPeopleState({ newList })
    console.log(JSON.stringify(peopleState))
  }

  let listOfPeople = null;
  listOfPeople = (
    <div>
      {
        peopleState.people.map((p, index) => {
        return <Person name={p.name} address={p.address} phone={p.phone}
          changed={(event) => changeHandler(event, index)} />

      })}
    </div>)

  return (
    <div>
      <h1>Example</h1>
      {listOfPeople}
    </div>
  );
}

export default App;

//For Person.js
import React from 'react';
import './Person.css';

const person = (props)=>{
    return(
        <div className="Person">
            <table>
                <tr>
                    <td>{props.name}</td>
                    <td>{props.address}</td>
                    <td>{props.phone}</td>
                    <td><input type="text" onChange={props.changed} value={props.name} /></td>
                </tr>    
            </table>    
        </div>
    );
}

export default person;

By the way, do I have to clone the objects of the state if I am doing updates?

1 answer

  • answered 2019-05-15 19:28 Christopher Ngo

    You can accomplish the appearance of double-binding like this

    Also here is the sandbox if you want to see it in action: https://codesandbox.io/s/7345kyly21

    import React, { useState, useEffect } from "react";
    import Person from "./Person";
    import ReactDOM from "react-dom";
    
    const App = props => {
      const [peopleState, setPeopleState] = useState({
        people: [
          { id: "001", name: "John Smith", address: "Main AV", phone: "312312" },
          { id: "002", name: "Marc Muller", address: "Second AV", phone: "345435" },
          { id: "003", name: "Sam Lopez", address: "Thrid AV", phone: "456444" },
          { id: "004", name: "Peter McNiece", address: "Road AV", phone: "213456" }
        ]
      });
    
      const changeHandler = (event, index) => {
        const newList = peopleState.people.map((person, pIndex, array) => {
          if (index === pIndex) {
            return {
              ...person,
              name: event.target.value
            };
          } else {
            return person;
          }
        });
    
        setPeopleState({ people: newList });
      };
    
      useEffect(() => {
        console.log(peopleState);
      }, [peopleState]);
    
    
      const renderList = () => {
        return (
          <div>
            {peopleState.people.map((p, index) => {
              return (
                <Person
                  name={p.name}
                  address={p.address}
                  phone={p.phone}
                  changed={event => changeHandler(event, index)}
                />
              );
            })}
          </div>
        );
      };
    
      return (
        <div>
          <h1>Example</h1>
          {renderList()}
        </div>
      );
    };
    

    Also on the same note, the reason why you are getting undefined map error is because of this:

    setPeopleState({ newList })
    

    When you pass in arguments like this, you are passing in a new key-value. Your state variable becomes { newList: [arrayofpeople]} and you still try to map over peopleState.people in render. Where .people is no longer a key in your state, which is why you are getting undefined.