I have
function Meal(props) {
const [isFavorite, setIsFavorite] = useState(false);
const setFavorite = () => {
setIsFavorite(currentStatus => !currentStatus);
};
render (etc...)
}
When Calling setFavorite, the isFavorite is not changed. Why?
I have read a lot of questions on StackOverflow around it and I still do not get the logic. I tried using useEffect but without success.
try it like this:
> function Meal(props) {
> const [isFavorite, setIsFavorite] = useState(false);
>
> const handleFavorite = () => {
> setIsFavorite(!isFavorite);
> };
> render (etc...) }
where handleFavorite function triggers the action (if it's called onClick, onChange or something else)
Related
Like the title explains, I can't seem to get the next set of results/page on request using react-query infiniteQuery. I also made a snack from their example code but still nothing.
It looks like the pageParam never gets incremented when fetchNextPage is called. I've tried version 3 and 4 with same results.
So I'm not sure if I should be incrementing the nextCursor manually. I can see nothing in the docs that suggests that I have to do so.
snack example
const fetchProjects = async ({ pageParam = 0 }) => {
const res = await fetch(`https://api.instantwebtools.net/v1/passenger?
page=${pageParam}&size=10`)
return res.json()
}
const List = () => {
const {
data,
error,
fetchNextPage,
hasNextPage,
isFetching,
isFetchingNextPage,
status,
} = useInfiniteQuery({
queryKey: ['test'],
queryFn: fetchProjects,
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
});
const loadMore = () => {
fetchNextPage()
}
Turns out the API I was using as test does not return any way for me to tell if there is more data to fetch. ie. no nextCursor property.
Suppose we have this scenario: we need to make a request to an API to get data on number of books available. If books > 0, we must trigger
some sort of popup with a function which is provided to us. The books need to be stored into the redux store for other components to use. The codebase already uses redux-thunk and redux-thunk-middleware.
What would be the best implementation using a hook and why? (displayPopUp is the function that we must use to trigger the pop-up)
1)
const useBooksNotification = () => {
const dispatch = useDispatch();
const [shouldShowNotification, setShouldShowNotification] = useState(false);
const books = useSelector(selectAvailableBooks);
useEffect(() => {
if (shouldShowNotification) {
setShouldShowNotification(false);
if (books.length > 0) {
displayPopUp();
}
}
}, [shouldShowNotification, books]);
const showNotification = async () => {
if (!shouldShowNotification) {
await dispatch(fetchBooks);
setShouldShowNotification(true);
}
};
return {
showNotification,
};
};
or 2)
const useBooksNotification2 = () => {
const dispatch = useDispatch();
const showNotification = async () => {
{
const response = await dispatch(fetchBooks);
const books = response.value;
if (books.length > 0) {
displayPopUp();
}
}
};
return {
showNotification,
};
};
Personally, I prefer 2 since to me it is much more readable but someone told me 1 is preferable i.e listening to the selector for the books instead of getting the books directly from the action/API response. I am very curious as to why this is? Or if there is an even better implementation.
I would understand using a selector if there was no displayPopUp function given to us and instead there was some implementation like so:
const BooksNotification = () => {
const dispatch = useDispatch();
const books = useSelector(selectAvailableBooks);
const showNotification = () => {
dispatch(fetchBooks);
};
return books.length > 0 ? <h1>Pretend this is a pop-up</h1> : null;
};
const SomeComponent = () => {
<div>
<h1>Component</h1>
<BooksNotification />
</div>
}
When kept in the component body, the following code works fine. Inside useEffect, it checks the asyncstorage and dispatches an action (the function is longer but other checks/dispatches in the function are of the same kind - check asyncstorage and if value exists, dispatch an action)
useEffect(() => {
const getSettings = async () => {
const aSet = await AsyncStorage.getItem('aSet');
if (aSet) {
dispatch(setASet(true));
}
};
getSettings();
}, [dispatch]);
I'm trying to move it to a custom hook but am having problems. The custom hook is:
const useGetUserSettings = () => {
const dispatch = useDispatch();
useEffect(() => {
const getSettings = async () => {
const aSet = await AsyncStorage.getItem('aSet');
if (aSet) {
dispatch(setASet(true));
}
};
getSettings();
}, [dispatch]);
};
export default useGetUserSettings;
Then in the component where I want to call the above, I do:
import useGetUserSettings from './hooks/useGetUserSettings';
...
const getUserSettings = useGetUserSettings();
...
useEffect(() => {
getUserSettings();
}, [getUserSettings])
It returns an error:
getUserSettings is not a function. (In 'getUserSettings()', 'getUserSettings' is undefined
I've been reading rules of hooks and browsing examples on the internet but I can get it working. I've got ESlint set up so it'd show if there were an invalid path to the hook.
Try the following.
useEffect(() => {
if (!getUserSettings) return;
getUserSettings();
}, [getUserSettings]);
The hook doesn't return anything, so it's not surprising that the return value is undefined ;)
I'm new to hooks and ran across this setup on SO and wanted to confirm that this is the correct pattern. I was getting the RN "unmounted component" leak warning message before and this seemed to solve it. I'm trying to mimic in some way compnentDidMount. This is part of a phone number verify sign up flow and onMount I want to just check for navigation and then fire off a side effect, set mounted true and then unmount correctly.
const SMSVerifyEnterPinScreen = ({ route, navigation }) => {
const [didMount, setDidMount] = useState(false)
const { phoneNumber } = route.params
useEffect(() => {
if(navigation) {
signInWithPhoneNumber(phoneNumber)
setDidMount(true)
}
return () => setDidMount(false)
}, [])
if (!didMount) { return null }
async function signInWithPhoneNumber(phoneNumber) {
const confirmation = await auth().signInWithPhoneNumber('+1'+phoneNumber)
...
}
return (
...
)
}
RN 0.62.2 with react-nav 5 - thanks!
Since signInWithPhoneNumber is a async function and will setState you will see warning it the component is unmounted before the response is available
In order to handle such scenarios you can keep a variable to keep track whether its mounted or not and then only set state is the mounted variable is true
However you do not need to return null if component has unmounted since that doesn't accomplish anything. The component is removed from view and will anyways not render anything.
Also you do not need to maintain this value in state, instead use a ref
const SMSVerifyEnterPinScreen = ({ route, navigation }) => {
const isMounted = useRef(true)
const { phoneNumber } = route.params
useEffect(() => {
if(navigation) {
signInWithPhoneNumber(phoneNumber)
}
return () => {isMounted.current = false;}
}, [])
async function signInWithPhoneNumber(phoneNumber) {
const confirmation = await auth().signInWithPhoneNumber('+1'+phoneNumber)
...
}
return (
...
)
}
function FlashcardScreen() {
useFirestoreConnect('flashcards')
const projects = useSelector(state => {
state.firestore.data.flashcards
})
console.log(projects)
return(// some ui element)
}
My Question is when I console.log(state.firestore.data.flashcards) I get my data. But when I console.log(projects) I don't get it. Why? And does it have anything to do with redux-thunk?
In order to work, the arrow function in the selector should return some value e.g.
const projects = useSelector(state => {
return state.firestore.data.flashcards
})
Or simply remove the curly brackets
const projects = useSelector(state => state.firestore.data.flashcards)