react-query refetchOnWindowFocus in React Native not working - reactjs

I am trying to setup react-query's App Focus Refetch in React Native per: https://tanstack.com/query/v4/docs/react-native#refetch-on-app-focus
I have a custom hook which is just a wrapper for the default app focus event listener. However, when leaving and entering the app my queries don't seem to refetch. Do I need to configure anything else? It doesn't say it in the docs.
const [appActive] = useAppState();
useEffect(() => {
focusManager.setFocused(appActive);
}, [appActive])

I don't know if you're using the useAppState hook from the #react-native-community/hooks package, but if you are, the hook doesn't return an array.
As you can see here, it returns the type of AppStateStatus.
If that is indeed the case, then you need to set the focused state like this:
const state = useAppState();
useEffect(() => {
focusManager.setFocused(state === 'active');
}, [state])

Related

How to implement promise when updating a state in functional React (when using useState hooks)

I know similar questions are bouncing around the web for quite some time but I still struggle to find a decision for my case.
Now I use functional React with hooks. What I need in this case is to set a state and AFTER the state was set THEN to start the next block of code, maybe like React with classes works:
this.setState({
someStateFlag: true
}, () => { // then:
this.someMethod(); // start this method AFTER someStateFlag was updated
});
Here I have created a playground sandbox that demonstrates the issue:
https://codesandbox.io/s/alertdialog-demo-material-ui-forked-6zss6q?file=/demo.tsx
Please push the button to get the confirmation dialog opened. Then confirm with "YES!" and notice the lag. This lag occurs because the loading data method starts before the close dialog flag in state was updated.
const fireTask = () => {
setOpen(false); // async
setResult(fetchHugeData()); // starts immediately
};
What I need to achieve is maybe something like using a promise:
const fireTask = () => {
setOpen(false).then(() => {
setResult(fetchHugeData());
});
};
Because the order in my case is important. I need to have dialog closed first (to avoid the lag) and then get the method fired.
And by the way, what would be your approach to implement a loading effect with MUI Backdrop and CircularProgress in this app?
The this.setState callback alternative for React hooks is basically the useEffect hook.
It is a "built-in" React hook which accepts a callback as it's first parameter and then runs it every time the value of any of it's dependencies changes.
The second argument for the hook is the array of dependencies.
Example:
import { useEffect } from 'react';
const fireTask = () => {
setOpen(false);
};
useEffect(() => {
if (open) {
return;
}
setResult(fetchHugeData());
}, [open]);
In other words, setResult would run every time the value of open changes,
and only after it has finished changing and a render has occurred.
We use a simple if statement to allow our code to run only when open is false.
Check the documentation for more info.
Here is how I managed to resolve the problem with additional dependency in state:
https://codesandbox.io/s/alertdialog-demo-material-ui-forked-gevciu?file=/demo.tsx
Thanks to all that helped.

Can I await the update function of the useState hook

I am building an app to understand the useState hook. This app simply has a form for entering username. I am trying to save the entered username. So, I have used react useState. And I tried to await the updating function of the useState in the event handler.
const usernameChangeHandler = async (event) => {
await setEnteredUsername(event.target.value);
console.log(enteredUsername, enteredAge);
};
And when I tried to log the username it doesn't show us the current state but the previous state. Why?
const usernameChangeHandler = async (event) => {
await setEnteredUsername(event.target.value);
console.log(enteredUsername, enteredAge);
};
enteredUsername is never going to change. It's a closure variable that's local to this single time you rendered the component. It's usually a const, but even if it was made with let, setEnteredUsername does not even attempt to change its value. What setEnteredUsername does is ask react to rerender the component. When the render eventually happens, a new local variable will be created with the new value, but code from your old render has no access to that.
If you need to run some code after calling setEnteredUsername, but you don't actually care if the component has rerendered yet, the just use the value in event.target.value, since you know that's going to be the new value of the state:
const usernameChangeHandler = (event) => {
setEnteredUsername(event.target.value);
console.log(event.target.value, enteredAge);
}
If instead you need to make make sure that the component has rerendered and then do something after that, you can put your code in a useEffect. Effects run after rendering, and you can use the dependency array to make it only run if the values you care about have changed:
const [enteredUsername, setEnteredUsername] = useState('');
useEffect(() => {
console.log('rendering complete, with new username', enteredUsername);
}, [enteredUsername]);
const usernameChangeHandler = (event) => {
setEnteredUsername(event.target.value);
};
the act of setting state is asynchronous; therefore, console logging directly after setting your state will not accurately provide you with how state currently looks. Instead as many have suggested you can utilize the useEffect lifecycle hook to listen for changes in your enteredUserName state like so:
useEffect(() => {
console.log(enteredUsername);
}, [enteredUsername]);
listening for changes within the useEffect will allow you to create side effects once state has updated and caused your component to rerender. This in turn will trigger your useEffect with the enteredUsername dependency, as the enteredUserName state has changed.

I've got a problem while using react hooks

I've got a problem while using react hooks.
const { formData, onChange, errors } = useContext(ResearchTaskContext);
const [annotations, setAnnotations] = useState('');
useEffect(() => {
onChange('formData.expenses.annotations', annotations);
}, [annotations]);
As you can see, when I change local state, need to change context state too. This is creating mode. Now I need fetch and prefilled the data for updating mode. in order to do it, I've added new useEffect hook.
useEffect(() => {
setAnnotations(formData.expenses.annotations);
}, [formData.expenses.annotations]);
But this code occur too many renders.
All context states are empty initially, so if I remove dependencies, fetch data can't affect.
You're essentially creating an infinite loop, I'd recommend to do away with annotations state as a whole, and use the formData.expenses.annotations as your source of truth.

Trying to understand the use of useCallback in this situation with React Navigation

I'm trying to learn why it's necessary to use useCallback in this situation:
function MyComponent() {
const navigation = useContext(NavigationContext);
const redirect = useCallback(() => {
navigation.navigate("Home");
});
useEffect(() => {
redirect();
}, [redirect]);
}
I can't use navigation directly inside useEffect() and also I can't refer the function without useCallback(). I don't know why I can use other objects like Firebase Context (from a Firestore instance) without any problem inside useEffect, but I can't use navigation directly.
From a quick look in the equivalent codebases:
Firebase Context provider is returned memoized
whilst the react-navigation one just returns the object.

Re-render component when navigating the stack with React Navigation

I am currently using react-navigation to do stack- and tab- navigation.
Is it possible to re-render a component every time the user navigates to specific screens? I want to make sure to rerun the componentDidMount() every time a specific screen is reached, so I get the latest data from the server by calling the appropriate action creator.
What strategies should I be looking at? I am pretty sure this is a common design pattern but I failed to see documented examples.
If you are using React Navigation 5.X, just do the following:
import { useIsFocused } from '#react-navigation/native'
export default function App(){
const isFocused = useIsFocused()
useEffect(() => {
if(isFocused){
//Update the state you want to be updated
}
}, [isFocused])
}
The useIsFocused hook checks whether a screen is currently focused or not. It returns a boolean value that is true when the screen is focused and false when it is not.
React Navigation lifecycle events quoted from react-navigation
React Navigation emits events to screen components that subscribe to them. There are four different events that you can subscribe to: willFocus, willBlur, didFocus and didBlur. Read more about them in the API reference.
Let's check this out,
With navigation listeners you can add an eventlistener to you page and call a function each time your page will be focused.
const didBlurSubscription = this.props.navigation.addListener(
'willBlur',
payload => {
console.debug('didBlur', payload);
}
);
// Remove the listener when you are done
didBlurSubscription.remove();
Replace the payload function and change it with your "refresh" function.
Hope this will help.
You can also use also useFocusEffect hook, then it will re render every time you navigate to the screen where you use that hook.
useFocusEffect(()=> {
your code
})
At the request of Dimitri in his comment, I will show you how you can force a re-rendering of the component, because the post leaves us with this ambiguity.
If you are looking for how to force a re-rendering on your component, just update some state (any of them), this will force a re-rendering on the component. I advise you to create a controller state, that is, when you want to force the rendering, just update that state with a random value different from the previous one.
Add a useEffect hook with the match params that you want to react to. Make sure to use the parameters that control your component so it rerenders. Example:
export default function Project(props) {
const [id, setId] = useState(props?.match?.params?.id);
const [project, setProject] = useState(props?.match?.params?.project);
useEffect(() => {
if (props.match) {
setId(props.match?.params.id);
setProject(props.match?.params.project);
}
}, [props.match?.params]);
......
To trigger a render when navigating to a screen.
import { useCallback } from "react";
import { useFocusEffect } from "#react-navigation/native";
// Quick little re-render hook
function useForceRender() {
const [value, setValue] = useState(0);
return [() => setValue(value + 1)];
}
export default function Screen3({ navigation }) {
const [forceRender] = useForceRender();
// Trigger re-render hook when screen is focused
// ref: https://reactnavigation.org/docs/use-focus-effect
useFocusEffect(useCallback(() => {
console.log("NAVIGATED TO SCREEN3")
forceRender();
}, []));
}
Note:
"#react-navigation/native": "6.0.13",
"#react-navigation/native-stack": "6.9.0",

Resources