Brandon M
Brandon M

Reputation: 13

TypeError: Can't read property 'map' of undefined when component uploads a file in React

React newb here. When props.submitClk === true (the green light that my upload button has been clicked and a file is sent from another component's form) , I'm trying to update state setList with a new technique object, but for now just, 'SomeObject'. I then get a "TypeError Can't read property 'map' of undefined" after click. I tried messing around with some conditionals, but no luck after a few hours of trying. Is this a async issue and the map is happening too soon? handleRemove works fine when I'm deleting objects. If I remove the function under the if statement, it will not throw an error on form submit. Also, the object is going to be larger, that it is why its currently nested this way. Thanks for any answers.

function SampleData(props) {
const [list, setList] = React.useState( [
        {
            closedguard: [
                {
                    name: "Armbar",
                    link: "",
                    x: "X",
                    id: "1",
                },
                {
                    name: "Triangle",
                    link: "",
                    x: "X",
                    id: "2",
                },
                {
                    name: "Omaplata",
                    link: "",
                    x: "X",
                    id: "3",
                },
        ] } 
    ] );  
 
    if( props.submitClk === true) {
        setList([...list, 'SomeObject'])
      }

  const handleRemove = (id) => {
    const newList = list[0].closedguard.filter(move => move.id !== id);
    setList([{ closedguard: newList }]);
};

    return (
        <div className="guard">
    
            <ul className="techUl">
                {" "}
                Closed Guard
                {list.map((pos) =>
                        pos.closedguard.map((move, id) => (
                            <li
                                key={id}
                                className="techLi"
                                                
                                >{move.name}
                                <span onClick={(id) => handleRemove(move.id)}> {move.x}</span>
                            </li>
                        ))
                    )}
            </ul>       
        </div>
    );
}

Upvotes: 1

Views: 128

Answers (3)

Rmcr714
Rmcr714

Reputation: 209

for updating the closeguard array of ur state list u can see @Nafis answer but u should make use of useEffect hook for running sideeffects. try putting this logic coupled with @Nafis answer for updating the state -

if( props.submitClk === true) { setList([...list, 'SomeObject']) }

inside an useEffect hook like shown below

useEffect(()=>{
if( props.submitClk === true) {
    setList([...list, 'SomeObject']) }

},[list])

Now modify the list.map function as below, then u wont get map undefined error

{list.length > 0 && list.map((pos) =>
                    pos.closedguard.map((move, id) => (
                        <li
                            key={id}
                            className="techLi"
                                            
                            >{move.name}
                            <span onClick={(id) => handleRemove(move.id)}> {move.x}</span>
                        </li>
                    ))
                )}

let me know if it works or not

Upvotes: 1

rustyBucketBay
rustyBucketBay

Reputation: 4561

If you wanted to update the nested object to add elemets to your closedguard: property array, check @Nafis answer.

What I believe is the problem is that the new objects you add do not have the closedguard property, so the map function cannot map that object.

If you add an object with that property it will work, for example:

const objToAdd = {closedguard: [
    {
    name: "newObj1",
    link: "",
    x: "X",
    id: "3",
   },
    {
      name: "newObj2",
      link: "",
      x: "X",
      id: "3",
    }] 
  }

If what you are doing is adding objects with another property, what you might need to map is the corresponding key, to be sure that you are getting a property out of the object, so would need to channge

pos.closedguard.map((move, id) => (

by:

pos[Object.keys(pos)[0]].map((move, id) => ( for example.

So in case your object doesnt have the closedguard, the first property of that object would be mapped.

Posting the full return with my proposal just to leave my proposal clear:

return (
    <div className="guard">
      <ul className="techUl">
        {" "}
        Closed Guard
        {list.map((pos) =>
          pos[Object.keys(pos)[0]].map((move, id) => ( //CHANGE HERE
            <li
              key={id} className="techLi"         
              >{move.name}
              <span onClick={(id) => handleRemove(move.id)}> {move.x}</span>
            </li>
          ))
        )}
      </ul> 
      <button onClick={handleOnClick}>Button</button>      
  </div>
  );

Upvotes: 0

Nafis
Nafis

Reputation: 1030

First without someObject you'll face Too many re-renders. React limits the number of renders to prevent an infinite loop.

Then you're updating nested object:

[
        {
            closedguard: [
                {
                    name: "Armbar",
                    link: "",
                    x: "X",
                    id: "1",
                },
                {
                    name: "Triangle",
                    link: "",
                    x: "X",
                    id: "2",
                },
                {
                    name: "Omaplata",
                    link: "",
                    x: "X",
                    id: "3",
                },
            ] 
        }
]

array of object, then object closedguard is inside which is an array.

So what you can do inside condition is

// think is the some objects
const newObject = [
      {
        name: "test1",
        link: "",
        x: "X",
        id: "5"
      },
      {
        name: "test2",
        link: "",
        x: "X",
        id: "56"
      }
    ];
    setList([{ closedguard: [...list[0].closedguard, ...newObject] }]);

instead of

setList([...list, 'SomeObject'])

Upvotes: 1

Related Questions