KUMAR SUBHAM
KUMAR SUBHAM

Reputation: 19

React Hook useEffect issue with setState

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

Upvotes: 1

Views: 12954

Answers (4)

Sohail Ashraf
Sohail Ashraf

Reputation: 10569

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

Upvotes: 0

Ori Drori
Ori Drori

Reputation: 191966

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])

Upvotes: 0

harisu
harisu

Reputation: 1406

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


Upvotes: 3

Keith
Keith

Reputation: 24181

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

Upvotes: 0

Related Questions