Reputation: 19
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
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
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
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
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