i'm trying to include a favorites mechanism to my react native codes but i'm facing a problem which i can't solve for hours. The situation here is, When the page renders first, favs.includes function works as expected. But when i clicked to favorite button, it raises an error "TypeError: favs.includes is not a function. (In 'favs.includes(id)', 'favs.includes' is undefined)" i tried something but none of them worked. Need some help..
const getFavs = async () =>{
await AsyncStorage.getItem('favs').then((res)=>{
if(res!==[]&&res!==null&&res!=='')
{
return setFavs(JSON.parse(res))
}
else
{
return setFavs([])
}
})
}
useEffect( () => {
getFavs();
console.log(favs.includes(id))
return console.log(typeof(favs))
;
}, [])
return (
<View style={{alignItems:'center',justifyContent: 'center',}}>
{favs.includes(id)?
<TouchableOpacity onPress={()=>{
AsyncStorage.removeItem('favs',id);
}}>
<Icon name = "star" size={25} color="orange" solid />
</TouchableOpacity >
:
<TouchableOpacity
style={{height:100,width:100,backgroundColor:'red'}} onPress={()=>{
console.log(typeof(favs))
setFavs(favs.push(id))
console.log(typeof(favs))
AsyncStorage.setItem('favs',JSON.stringify(favs))
setFav(true)
}}>
<Icon name = "star" size={25} color="orange" />
</TouchableOpacity >
}
</View>
)
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
The problem lies in this line in your click handler
setFavs(favs.push(id))
which sets favs
to a number not an array. Array#push()
returns the new length of the array after push, not the array itself. see Why does Array.prototype.push return the new length instead of something more useful?.
Also, using push()
on a state array mutates the array, you should instead
setFavs(prevFavs => [...prevFavs, id])
// or
setFavs(prevFavs => prevFavs.concat(id))
Your last error is assuming that calling console.log
after your setState
call will reflect the change in state when it will in fact log the old(current) state value. The updated state value won't be available until the next render cycle. see Why calling react setState method doesn't mutate the state immediately?
CodePudding user response:
I think it is usually because of when the component rendered at first, favs
state is still empty or getFavs
async function wasn't finished.
Maybe you can try to give condition to where favs.includes
invoked such as:
{favs && favs.includes && favs.includes(id)? // make sure favs is truthy and favs.includes property existed
<TouchableOpacity onPress={()=>{
AsyncStorage.removeItem('favs',id);
}}>
<Icon name = "star" size={25} color="orange" solid />
</TouchableOpacity >
:
<TouchableOpacity
style={{height:100,width:100,backgroundColor:'red'}} onPress={()=>{
console.log(typeof(favs))
setFavs(favs.push(id))
console.log(typeof(favs))
AsyncStorage.setItem('favs',JSON.stringify(favs))
setFav(true)
}}>
<Icon name = "star" size={25} color="orange" />
</TouchableOpacity >
}
Please let me know if this solve your issue or not, thanks
CodePudding user response:
I see two problems with your code.
Firstly, I do not see your useState
usage. It should be
const [favs, setFavs] = useState([]);
Secondly, you are mixing asynchronous and synchronous code. You are calling getFavs
, which contains a call to asynchronous storage which is executed "outside" of main program thread. And immediately after that you are calling favs.includes()
, which of coure will not work, because the async call did not come through yet.