在useEffect()中修改对象不会更新状态

时间:2020-07-21 13:33:37

标签: promise axios

我是React和Hooks的新手。我试图从某个API中获取一个对象,对其进行转换(基于另一个API的结果),并更新useEffect挂钩内的状态。但是,渲染DOM时不会反映对象转换。这是我的代码。

  useEffect(() => {
    let modified_movies = []
    axios.get(LOCAL_MOVIE_API)
      .then(res => {
        const movies = res.data.content;
        movies.map(async movie => {
          const response = await axios.get(_API,
            {
              params: {
                query: movieTitle,
                year: movie.movieYear
              }
            });
            const poster = response.poster; //add property to object
            const modified_movie = { ...movie, poster_url: poster };
            modified_movies.push(modified_movie)
        })
        setMovies(modified_movies) //
      })
  }, [])

我也没有就地修改对象,因此不能复制对象,但是react仍然不会用修改后的对象更新状态,并且新属性不会反映在视图中。是我遗漏了明显的东西还是有更好的方法实现这一目标?

4 个答案:

答案 0 :(得分:2)

与反应无关。

您的movies.map(async movie => {...调用正在创建Promise,将在您的setMovies调用之后执行,因此modified_movies数组将为空。因此,您需要等待map函数完成,然后设置状态。

解决问题的一种方法:

useEffect(() => {
    let modified_movies = []
    axios.get(LOCAL_MOVIE_API)
      .then(res => {
        const movies = res.data.content;
        const moviePromises = movies.map(async movie => await axios.get(_API,
            {
              params: {
                query: movieTitle,
                year: movie.movieYear
              }
            })
        ) // map all the calls into one array 
        Promise.all(moviePromises).then((allResponses) => {
            // allResponses will be an array of values, which you can map to your needs
            setMovies(allResponses) //
        })
      })
  }, [])

以下是一些有用的文档:

Promise.all

Array.map

答案 1 :(得分:1)

这与React或useEffect无关。

您只是对Promise有问题。

考虑以下示例:

// ----------- YOUR   WAY --------------
const getRandomValue = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(Math.random());
    }, 10);
  });
};

const movies = [1, 2, 3];

const modified_movies = [];

movies.map(async(movie) => {
  modified_movies.push(await getRandomValue());
});

console.log("modified movies with map", modified_movies);

// ---------- One of correct ways -----------------

const Movies = [1, 2, 3];

const modifiedMovies = [];

const getModifiedMovies = async() => {
  for (let movie in movies) {
    modifiedMovies.push(await getRandomValue());
  }

};

getModifiedMovies().then(() => {
  console.log("modified movies with for let", modifiedMovies);
});

第一种方法模拟了您的方式,总是返回一个空数组。

第二种方法等待所有诺言得到解决,从而返回正确的诺言。

回到您的问题,我认为代码应类似于以下内容:

axios.get(LOCAL_MOVIE_API)
  .then(async(res) => {
    const movies = res.data.content;
    for (const movie of movies) {
      const response = await axios.get(_API, {
        params: {
          query: movieTitle,
          year: movie.movieYear
        }
      });
      const poster = response.poster; //add property to object
      const modified_movie = { ...movie,
        poster_url: poster
      };
      modified_movies.push(modified_movie);
    }
    setMovies(modified_movies);
  });

答案 2 :(得分:-2)

您需要提供应在哪个状态更改上触发useEffect,如下所示:

useEffect(() => {
      // your logic here
      })
  }, [theStateWhichChanged])

答案 3 :(得分:-2)

您的依赖项数组为空,这意味着useEffect内部的代码在组件安装时执行,并且仅在此时间执行。 对我来说,代码看起来过于复杂且缺少部分(movieTitle,movie.movi​​eYear来自何处?)。建议您将请求拆分到useEffect内的函数中,以使自己更加清楚。

如果状态取决于效果(例如另一个状态),则需要将i添加到依赖项数组中:

React.useEffect(() => {
  // your code here
}, [movies])