简体   繁体   中英

How to update state array fetched from API in React Hooks?

I'm fetching data from Studio Ghibli API and I am able to successfully fetch it, set the state array of objects and render it in my presentational component. However, I'm trying to create a function which will add new property "keyword" to every object in my state array. The problem is that when i try to copy the state array to manipulate it in my createKeywords function, the returned copy is empty and I'm unable to manipulate it after it being set.

This is the relevant code:

  const baseUrl = 'https://ghibliapi.herokuapp.com/'
  const [hasError, setErrors] = useState(false)
  const [movies, setMovies] = useState([])

  useEffect(() => {
    fetch(baseUrl + 'films')
      .then((res) => res.json())
      .then((res) => {
        console.log(res);
        setMovies(res)
        createKeywords()
      })
      .catch(err => setErrors(true));
  }, [])

  const createKeywords = () => {
    const moviesWithKeywords = [...movies]
    moviesWithKeywords.forEach(function(movie){
      movie.keyword = 'castle'
    }); 
    setMovies(moviesWithKeywords)
}

If i don't call the createKeywords function everything works fine but obviously copying and setting new movies state creates problem. I tried adding [movies] instead of empty array in useEffect and that works but then useEffect runs indefinitely. Thank you in advance, React isn't my strong suit!

The solution seems might not be very obvious. There are cases where setMovies (in general setting the state) is an async operation, which means that even if you setMovies the movies variable is not being updated quite fast and therefore you are already executing the createKeawords function. This means that within the keywords function the movies variable didn't have the chance to update fast enough. I would recommend to pass the res as a parameter in the createKeywords and use this variable to copy the array to the moviesWithKeywords .

Have a look here under the section State Updates May Be Asynchronous

So do something like that:

  const baseUrl = 'https://ghibliapi.herokuapp.com/'
  const [hasError, setErrors] = useState(false)
  const [movies, setMovies] = useState([])

  useEffect(() => {
    fetch(baseUrl + 'films')
      .then((res) => res.json())
      .then((res) => {
        console.log(res);
        setMovies(res)
        createKeywords(res)
      })
      .catch(err => setErrors(true));
  }, [])

  const createKeywords = (movies) => {
    const moviesWithKeywords = [...movies]
    moviesWithKeywords.forEach(function(movie){
      movie.keyword = 'castle'
    }); 
    setMovies(moviesWithKeywords)
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM