【问题标题】:React Hook useEffect issue with setState使用 setState 反应 Hook useEffect 问题
【发布时间】:2020-05-11 01:05:56
【问题描述】:

我正在使用 useState ,它有 2 个数组 imageList 和 videoList ,然后在 useEffect 钩子中我在数据上使用 forEach 然后如果类型是 image 然后将 item 推送到 image 。 但最后我没有得到数组的图像列表或视频列表。

const [list, setDataType] = useState({imageList:[], videoList:[] });
  useEffect (()=>{
    //data is list of array
      dataList.forEach(item =>{

       if(!item.images)  {
         setDataType({...list, imageList:item})
       } 
       else if (item.images[0].type === "video/mp4")
       {
        setDataType({...list, videoList :item})
       }
       else if((item.images[0].type === "images/gpeg")
       {
          setDataType({...list, imageList:item})
       }
      })
  },);

这里的类型检查工作正常,但最后我得到了最后一次获取的数据,它可以是 videolist 或 imageList 最后我应该得到所有类型为图像的图像列表和类型为视频的相同视频列表

【问题讨论】:

  • 不要在循环中不断调用setState,建立你的状态然后设置它。
  • 是的,您可以提取两个不同数组中的所有图像和视频,然后调用 setState 将其持久化。在循环中调用 setState 并不是一个有效的解决方案

标签: javascript reactjs ecmascript-6 react-hooks


【解决方案1】:

避免在循环内设置状态。在每个 setState 上,React 都会重新渲染组件。所以建议先准备好你的状态,然后再更新状态。

const [list, setDataType] = useState({imageList:[], videoList:[] });
  useEffect (() =>{
      let newState = {imageList:[], videoList:[] };
      dataList.forEach(item =>{

            if(!item.images)  {
                newState.imageList.push(item);
            } else if (item.images[0].type === "video/mp4") {
                newState.videoList.push(item);
            } else if((item.images[0].type === "images/gpeg") {
                newState.imageList.push(item);
            }
      });
      setDataType(prevState => {
        return {
            imageList: newState.imageList, 
            videoList: newState.videoList 
        }
     });
},[]);

【讨论】:

  • 在这一行 setDataType(preState => {...preState, imageList:item}) 说格式错误
  • 无限循环
  • 当组件在 useEffect 中调用 setState 时,可能会发生这种情况,但 useEffect 要么没有依赖数组,要么每次渲染时有一个依赖项发生变化。
  • 谢谢你,它成功了。我看到你的网站很棒,
  • 谢谢@Kumar
【解决方案2】:

理想情况下,您不希望在循环中不断调用 setState。建立状态,然后设置它。

这里有两个过滤器可能对你有用..

例如。

const [list, setDataType] = useState({imageList:[], videoList:[] });
  useEffect (()=>{
    //data is list of array
    setDataType({
      videoList: dataList.filter(item => item.images[0].type === 'video/mp4'),
      imageList: dataList.filter(item => item.images[0].type === 'images/gpeg')
    });
  },);

【讨论】:

  • 只有视频列表正在获取数据,并且 useEffect 正在无限循环中进行
  • 是的,useEffect 没有依赖关系.. 这次尝试-> }, []); 你的意思是image/jpeg 吗?
【解决方案3】:

在循环中调用 setState 不是正确的方法。下面是尝试使用数组方法过滤器在功能上构造列表的解决方案。

const [list, setDataType] = useState({ imageList: [], videoList: [] });
useEffect(() => {
    let videos = dataList.filter(item => item.images[0].type === "video/mp4")
    let images = dataList.filter(item => item.images[0].type === "images/gpeg")
    setDataType({imageList: images, videoList: videos})
}, []);


【讨论】:

  • 工作但 useEffect 进入无限循环 "index.js:1 警告:超过最大更新深度。当组件在 useEffect 中调用 setState,但 useEffect 没有依赖数组时,可能会发生这种情况,或每次渲染的依赖项之一发生变化
  • 你在useEffect的末尾添加了空数组[]依赖吗?
  • @KUMARSUBHAM 你也可以这样做:[dataList]
  • 是的,如果您想在每次 datalist 数组更改时重新运行效果。 :thumbup:
【解决方案4】:

请勿使用 useEffectuseState 挂钩进行数据转换,这不是异步流程的一部分。

这种情况最好使用useMemo 处理。当datalist 改变时,useMemo 将产生list,如果它没有改变,则使用记忆的值。此外,它不会导致无限循环,因为它不会在监听自己的状态时导致重新渲染。

要创建list,请使用包含 mime 类型及其各自类型的 Map。减少item的数组,并根据你从Map中得到的类型,将它们推送到imagesvideos子数组中。

/** external code (not in the hook) **/
const types = new Map([['video/mp4', 'video'], ['images/gpeg', 'image']])

const getListType = ({ type }) => types.has(type) ? 
  `${types.get(type)}List` : null

/** hook code **/
const list = useMemo(() => dataList.reduce((r, item) => {
  const list = getListType(item.images[0])

  is(list) r[list].push(item)

  return r;
}, { imageList:[], videoList:[] }), [dataList])

【讨论】:

    猜你喜欢
    • 2021-08-02
    • 2021-10-10
    • 2020-11-05
    • 2020-08-09
    • 2020-12-20
    • 2020-07-21
    • 2021-05-16
    • 1970-01-01
    • 2021-01-16
    相关资源
    最近更新 更多