I have this component that renders the number of people that liked the book, however I cant get consistent result in my states.
Heres my code:
///this is for getting the book details
const {id} = useParams();
const dispatch = useDispatch();
const[hasliked, sethasliked] = useState(null);
useEffect(() => {
const bookData = {
id: id
}
dispatch(openBook(bookData))
}, [])
///and this is for setting the state of the component
const { bookFeatured, isLoading, isSuccess, openbook, likeStatus } = useSelector(state => state.book);
const { user, isError, message } = useSelector(state => state.auth);
useEffect(()=>{
if(openbook.noOfLikes?.find((noOfLikes) => noOfLikes === user._id)){
sethasliked(true)
}else{
sethasliked(false)
}
},[openbook])
const Likes = () => {
return hasliked
? (
<><FaHeart fontSize="small" /> {hasliked.toString()}</>
) : (
<><FaRegHeart fontSize="small" fill='white'/> {hasliked.toString()}</>
);
};
the thing here is i get inconsistent result when getting the value from const hasliked, if i already liked a book, it will sometimes tell me that the hasliked is false, but sometimes it will go to true, this caused me to get different result each time i render the component, I think it has something to do with the delay in useEffect due to async actions, Is there something that I can do about this?
.toString()? why not just do
return hasliked
? (
<><FaHeart fontSize="small" />True</>
seeing as how hasliked is already true there.
else{
sethasliked(false)
}
can be eliminated by changing the init to
const[hasliked, sethasliked] = useState(false);
dispatch(openBook(bookData)) is returning a promise you can set hasliked in a .then block eliminating the need for a second useeffectreturn hasliked
to this
return hasliked && openbook
You can add a loading state to your component and check if the loading state is true then show a loader or else show your component.
const {id} = useParams();
const dispatch = useDispatch();
const[hasliked, sethasliked] = useState(null);
const [loading, setLoading] = useState(false) // The loading state
useEffect(() => {
setLoading(true) // Setting the loading state true
const bookData = {
id: id
}
dispatch(openBook(bookData))
}, [])
///and this is for setting the state of the component
const { bookFeatured, isLoading, isSuccess, openbook, likeStatus } = useSelector(state => state.book);
const { user, isError, message } = useSelector(state => state.auth);
useEffect(()=>{
if(openbook.noOfLikes?.find((noOfLikes) => noOfLikes === user._id)){
sethasliked(true)
setLoading(false) // Setting loading state to false
}else{
sethasliked(false)
setLoading(false) // Setting loading state to false
}
},[openbook])
const Likes = () => {
// show a loader if loading is true
if(loading) {
return <div>Loading...</div>
}
return hasliked
? (
<><FaHeart fontSize="small" /> {hasliked.toString()}</>
) : (
<><FaRegHeart fontSize="small" fill='white'/> {hasliked.toString()}</>
);
};
Have you tried using setTimeout?
useEffect( () => {
const tmr = setTimeout( () => {
const bookData = { id: id }
dispatch( openBook(bookData) )
}, 500)
return () => clearTimeout(tmr);
}, [])
useEffect( () => {
const tmr = setTimeout( () => {
if (openbook.noOfLikes?.find( (noOfLikes) => noOfLikes === user._id) ) {
sethasliked(true)
} else { sethasliked( false) }
}, 500)
return () => clearTimeout(tmr);
},[openbook])