Array repeated in every single API element when map into an array

I map in an API, then I made input to make tags into every single element into this API individually. But when I implement the code when I write one tag in any element of API, it gets repeated into the other elements.

    const [tag, setTag] = useState("")
    const [tags, setTags] = useState([])
//================================
//======= THE HELPER FUNCTIONS
    function handleChange(event) {
        const newValue = event.target.value
        setTag(newValue)
    }

    function addTag(e) {
        e.preventDefault()
        setTags((prevTag) => {
            return [...prevTag, tag]

        })
        setTag("")
    }
//===========================================
//=============================================
                              {isOpen.includes(student.id) ? (
                                        <div>
                                            <ul>
//====  the problem is here when I put somthing in the tag array it's repeated in every single API objects
                                                {tags.map(tag => <li key={tag.id}>{tag}</li>
                                                )}
                                            </ul>
                                            <form onSubmit={addTag}>
                                                <input
                                                    type="text"
                                                    value={tag}
                                                    onChange={handleChange}
                                                    className="add-tag-input"
                                                    placeholder="Insert tag"
                                                />
                                            </form>
                                        </div>
                                ) : null}

You have several maps here. Can you be specific? Or better yet, can you reduce your issue down to the bare essentials. This can be a good diagnostic tool, too.

You have some helper functions in there and I have no idea what they are doing.

See if you can reduce this down tho the minimal reproducible version.


It’s not the issue you are facing, but this did catch my eye:

const gradLngth = student.grades.map(grades => grades).length

Why map here? By definition, this is the same as:

const gradLngth = student.grades.length

By definition, the mapped array will have the same length as the original. And this specific map is doing nothing but making a shallow copy and then abandoning it (it’s not stored).

Secondly, variable names - “gradLngth”? Why save the two characters? First of all, “grad” is a word in it’s own right, so that might cause confusion. Also, “length” is confusing to me here because you don’t care about the length of the array, you care how many grades there are. Sure they’re the same, but why not be specific. I would have called this “numGrades” or “gradeCount”.

Naming variables is very important. It is hard to read through thousands of lines of code. Anything that makes it even the tiniest of a sliver more difficult is bad. Well written code should read like a story with a as few comprehensional speed bumps as possible. It will also keep your coworkers from slashing your tires.

Also, things like this:

onChange={e => handleChange(e)}

can usually be written as:

onChange={ handleChange }

It know what is being passed in and if you are just taking the first parameter and passing it in as the first parameter, JS can figure that out for you.

1 Like

Thank you for your advice, it’s true. May be I need a lot of practicing.
I edited the question.

We all need a lot of practice.

Please don’t edit posts like that - it causes confusion to people trying to read the thread.

OK, so, you’re saying that after this:

        setTags((prevTag) => {
            return [...prevTag, tag]
        })

OK, that looks legit. The only comment I’d have is that “prevTag” is a bad name because it implies that there is only one when it is an array. It should be “prevTags”.

But OK, that looks fine.

I guess I’d ask what a tag looks like an object but occasionally you are treating it like a primitive, like here: <li key={tag.id}>{tag}</li> and value={tag}. Which is it?

Another issue might be your key - if it’s not unique, it can screw up reacts rendering. Instead of the above, could you try:

<li key={tag.id}>{tag.id}</li>

to see what you get?

:slightly_smiling_face: Thank you again I changed the name of the function.
Unfortunately, it doesn’t work after changing it to {tag.id}.
But I have an error when I expand the grades list using this button

<button className="expand-btn" onClick={() => toggleIsOpen(student.id)}>{isOpen.includes(student.id) ? '-' : '+'}</button>

The error

Warning: Each child in a list should have a unique "key" prop.

The helper function

const toggleIsOpen = (id) => {
        if (isOpen.includes(id)) {
            setIsOpen(isOpen.filter(stu_id => stu_id !== id))
        } else {
            let newOpen = [...isOpen]
            newOpen.push(id)
            setIsOpen(newOpen)
        }
    }

OK, that means that your ids aren’t unique. What are your ids? Somewhere can you do:

console.log(JSON.stringify(tags, null, 2));

so we can see what we’re dealing with?

It gives me an empty array. But the API already has an id (sequenced numbers). And when I put something into tag array it’s rendering every single alphabet I wrote.

I don’t understand. You have no data? What is it mapping? Need to see what is wrong. We can’t figure out what is wrong with your car unless you let us see under the hood.

Ok, I displayed API elements into my code. Now I need to display tags for each API element into an array so I can search for tags into all tags inside my app.

When I open the expand button, I have an error.

Warning: Each child in a list should have a unique "key" prop.

Then when I add tags into one API element it gets repeated in the other elements

The code is here:
https://codesandbox.io/s/eloquent-varahamihira-dwph1?file=/src/components/StudentsCards.js

When I open the expand button, I have an error.

Warning: Each child in a list should have a unique "key" prop.

It isn’t an error, it’s a warning. It won’t stop your app from working. But with unique keys, React can do a better job of deciding when to rerender you components. It is definitely a good idea to help React be more efficient. Figure out to which map this is referring and fix it - give them unique keys.

As far as the tags, I would need to see the code. The sandbox to which you linked is missing most of the code, especially the code I would have to see.

Is the API’s id not enough?.
I make a new link: https://codesandbox.io/s/eloquent-varahamihira-dwph1

I can answer that without even looking - no. If it were sufficient, then React wouldn’t be complaining. The other option is that React and/or JavaScript are broken and we are the first two people in the world to figure it out. I think that that’s extremely unlikely.

As to why you are getting duplicate values for your tags…

My first observations is a structural problem. You have one master component that handles the entire list and each individual card. So, that means that things like tags are shared between all the list components. I think that this should be StudentCardList and that you should have a subcomponent called StudentCard. There the state of each card could be handled separately. I would want to store those tags either in my students data structure or something that mimics it or at least in a way that differentiates to which student they are supposed to be attached. It could even be broken down further, but I think that this is a bare minimum. This component is just way to complicated and doing too many things. It defeats one of the great elegant purposes of React.

I think if you started logging out things like tags and isOpen you would see what is happening.

OK, why are you getting that “Warning: Each child in a list…” warning… If you look at this code:

{tags.map((tag) => (
  <li key={tag.id}>{tag}</li>
))}

So, tags is an array of strings so tag is a string. So, what does that make tag.id? If you understand that, you’ll understand why that warning is popping up.

While I’m here, looking at your JS and React:

Things like this:

    setTags((prevTags) => {
      return [...prevTags, tag];
    });

would usually be written:

    setTags(prevTags => [...prevTags, tag]);

It’s just takes up less room and people understand what is going on. If you don’t know about the implicit return of arrow functions, read up.


  <div></div>

Why?


const mapDispatchToProps = (dispatch) => {
  return {
    fetchStudents: () => dispatch(fetchStudents())
  };
};

The more common way to do this would be to use the object form of mdtp:

const mapDispatchToProps = {
  fetchStudents,
};

This is just a simple object, using object shorthand notation. Connect is smart enough to figure it out.

  return studentData.loading ? (
    <div>Loading...</div>
  ) : studentData.error ? (
    <h2>{studentData.error}</h2>
  ) : (
  // ...
              ) : null}

As near as I can tell, there is 70 lines between the start of that ternary and the end and the ternaries are chained and nested. That is insane. It is a puzzle for anyone trying to read it. Instead, why not use a return early pattern:

  if (studentData.loading) {
    return <div>Loading...</div>;
  }
  
  if (studentData.error) {
    return <h2>{studentData.error}</h2>;
  }

  return (
    <div>
      <form>
  // ...

That breaks those out and avoids having to finish the ternary 70 lines later. There are other patterns, but I think that that is the cleanest.

1 Like

I appreciated your adequate answers. I split the components. It’s working.
replying to your notes:
1- I added <div></div> to make grid styling.
2- {tags.map((tag) => (<li key={tag.id}>{tag}</li>))} you are right, but I added student.id instead, it still gives me the same warning.
3- When I used const mapDispatchToProps = { fetchStudents}; the code was broken.

Thanks for your advice about the clean code.
If I want to ask about adding tags as a property to the API, should I ask here or make another question?.

1- I added <div></div> to make grid styling.

There are better ways to do that, like with CSS for example. In some cases, I do do a <div /> but usually applying a class or styling to it to have it do something.

2- {tags.map((tag) => (<li key={tag.id}>{tag}</li>))} you are right, but I added student.id instead, it still gives me the same warning.

OK, but you still don’t seem to understand the underlying issue here - you need a unique ID for each element. student.id would not be unique for each element.

If you know that tag is a unique string (within each mapping), you could just use that:

<li key={tag}>{tag}</li>

Another thing which a lot of people do but is frowned upon, is to use an index:

(tag, idx) => (<li key={`${idx}`}>{tag}</li>)

It is considered bad React practice but it could work for now.

3- When I used const mapDispatchToProps = { fetchStudents}; the code was broken.

Without any details I cannot help with that, but this is standard redux practice.

If I want to ask about adding tags as a property to the API, should I ask here or make another question?.

I guess I don’t understand the question.

Here I have studentsCards component that has a child StudentCard component.
The StudentCard component has the arrays of tags. now I want to search into the arrays of tags.

first: what is better, to post these array of tags to the API or to just search into the arrays of tags?

Second: When I tried to post the arrays of tags it gives me this error Access to XMLHttpRequest at 'https://api.~' from origin 'http://localhost:3000' has been blocked by CORS policy
I tried another way by searching into the arrays of tags directly using splice, but I did not reach any result. So I got confused.

first: what is better, to post these array of tags to the API or to just search into the arrays of tags ?

I don’t know what your API is. But if the list of tags is on your client side already and you just need to search those, I can’t imagine why you would need to hit the server for that.

Second: When I tried to post the arrays of tags it gives me this error Access to XMLHttpRequest at 'https://api.~' from origin 'http://localhost:3000' has been blocked by CORS policy

Well, it sounds like you have a CORS issue which is a completely separate topic.

I tried another way by searching into the arrays of tags directly using splice, but I did not reach any result. So I got confused.

Wait, what? How do you search with splice? That is for manipulating arrays, removing or replacing elements. Why would you use that instead of find or findIndex or even includes?

1 Like

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.