1

I want to add items to an array with the useState hook instead of doing array.push. This is the original code:

   let tags = []
      data.blog.posts.map(post => {
        post.frontmatter.tags.forEach(tag => {
          if (!tags.includes(tag)){
            tags.push(tag)
          }
        })
      })

This is one of several things I've tried with React:

const [tags, setTags] = useState([])
  
  data.blog.posts.map(post => {
    post.frontmatter.tags.map(tag => {
      if (!tags.includes(tag)){
        setTags(tags => [...tags, tag])
      }
    })
  })

The "tags" state variable does not receive anything in the above example.

I have looked at a variety of similar threads but the problems and solutions there are difficult to translate to this situation.

6
  • What is your end goal here? How does data.blog.posts relate to the state atom? Or post.frontmatter.tags? Commented Feb 1, 2022 at 18:34
  • I would not add an setState inside a loop. Commented Feb 1, 2022 at 18:35
  • The goal is to conditionally add indices to the "tags" array from the "data.blog.posts" array. Each index in the post array has at least one tag - I only want to add it to "tags" if it has not already been added. Commented Feb 1, 2022 at 18:41
  • @CyrusZei Why not using a setter inside a loop? Is there any programmatical reason for avoiding it? Commented Jan 27, 2023 at 11:10
  • @BairDev because every time you set the state you render. So when you are looping over 10 items in an array you are render it 10 times Commented Jan 28, 2023 at 14:20

2 Answers 2

1

You can try setting the tags state in initial render or on any event as per your requirement .

const [tags, setTags] = useState([]);

useEffect(()=>{
  const arr=[];
  data.blog.posts.map(post => {
    post.frontmatter.tags.map(tag => {
      if (!arr.includes(tag)){
        arr.push(tag)
      }
    })
  });
 setTags([...arr]);
},[]);

Sign up to request clarification or add additional context in comments.

Comments

0

Ok, I did understand what you wanted to do.

Here is the code and I did add some commest and there is also a working code sandbox

so it will show the "tags" you have on your state and when you click on the button it will filter and add those tags that are missing

import React, { useState } from "react";

//mock data.blog.posts

const data = {
  blog: {
    posts: [
      {
        frontmatter: {
          tags: ["tag1", "tag2", "tag3"]
        }
      }
    ]
  }
};

const App = () => {
  const [tags, setTags] = useState(["tag1"]);

  const filterTags = () => {
    const myTags = ["tag1"];
    let result;
    data.blog.posts.map((post) => {
      // check what tags are not included in tag stateon line 18
      result = post.frontmatter.tags.filter((item) => !tags.includes(item));
    });
    // here it will show that 'tag2' and 'tag3' does not exist
    console.log("result", result);

    // here we are setting the state

    setTags((oldState) => [...oldState, ...result]);
  };

  return (
    <div className="App">
      <h1>My tags</h1>
      {tags.map((tag) => (
        <h4>{tag}</h4>
      ))}

      <button onClick={() => filterTags()}>add tags</button>
      <hr />
      <h1>My tags from posts</h1>
      {data.blog.posts.map((posts) => {
        return posts.frontmatter.tags.map((tag) => <div>{tag}</div>);
      })}
    </div>
  );
};

export default App;

and here is the codeSandBox

7 Comments

This doesn't work. I think setTags() triggers a re-render every time and therefore I get an infinite loop.
Ok, I did not test that code. let me create an snippet
ahh, now I see what you want to do. If the tag does not exist you want to add that tag.
Yes that is correct!
there you go, hope this will help
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.