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
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
Share Improve this question asked Jan 24, 2020 at 17:09 KUMAR SUBHAMKUMAR SUBHAM 191 gold badge1 silver badge9 bronze badges 2-
Don't call a
setState
constantly inside a loop, build your state then set it. – Keith Commented Jan 24, 2020 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 Commented Jan 24, 2020 at 17:18
4 Answers
Reset to default 3It 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})
}, []);
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')
});
},);
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])
Avoid set state inside a loop. On each setState, React will re-render the ponent. So remended 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
}
});
},[]);