1

I am using useState which has 2 array imageList and videoList and then in useEffect hook i am using forEach on data then if type is image then push to item to image . But at last i am not getting imagelist or video list of array.

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})
       }
      })
  },);

Here type check is working correctly but at last i get last fetch data only which can be videolist or imageList In last i should get list of all imageList whose type is image and same video list whose type is video

KUMAR SUBHAM
  • 19
  • 1
  • 1
  • 7
  • Don't call a `setState` constantly inside a loop, build your state then set it. – Keith Jan 24 '20 at 17:15
  • Yeah you can extract all the images and videos in two differnt arrays and then make one call to setState to persist it. Its not an efficient solution to be calling setState in a loop as you are doing – harisu Jan 24 '20 at 17:18

4 Answers4

3

It is not a proper way to call setState in a loop. Below is an attempted solution using array method filter to functionally construct the list.

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})
}, []);


harisu
  • 1,376
  • 8
  • 17
  • worked but useEffect goes in infinite loop "index.js:1 Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render – KUMAR SUBHAM Jan 24 '20 at 17:35
  • did you add the empty array [] dependency at the end of the useEffect? – harisu Jan 24 '20 at 17:37
  • 1
    @KUMARSUBHAM you could do this also: `[dataList]` – 7urkm3n Jan 24 '20 at 17:40
  • Yeah That is if you want to re-run the effect each time the datalist array changes. :thumbsup: – harisu Jan 24 '20 at 17:41
0

Avoid set state inside a loop. On each setState, React will re-render the component. So recommended to prepare your state first and then update the state.

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 
        }
     });
},[]);
Sohail Ashraf
  • 10,078
  • 2
  • 26
  • 42
0

Ideally you don't want to be calling setState constantly inside your loop. Build the state up and then set it.

Two filters might work for you here..

eg.

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')
    });
  },);
Keith
  • 22,005
  • 2
  • 27
  • 44
0

Don't use the useEffect & useState hooks for data transformation, which is not part of an asynchronous flow.

This case is better handled with useMemo. When the datalist changes, useMemo will produce the list, and if it doesn't change, the memoized value would be used. In addition, it won't cause an infinite loop, because it won't cause a rerender while listening to its own state.

To create list, use a Map that will hold the mime-types and their respective types. Reduce the array of items, and according to the type you get from the Map, push them in the images or videos sub-arrays.

/** 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])
Ori Drori
  • 183,571
  • 29
  • 224
  • 209