In React I can execute code every time a location
change using use-react-router which gives me access to the history
, location
, and match
properties. What I do is to use a useEffect
and run code every time location
property change, something like:
import useRouter from "use-react-router";
const { location } = useRouter();
React.useEffect(() => {
// run some code every time `location` change.
}, [location]);
There is a way to achieve this same behaviour with react-navigation in react-native?
Update 1 - Temporary solution using useNavigationState hook
I'm tryng to check if navigation state change and run code when that happend, to achieve this I've used useNavigationState from react-navigation
:
import {useNavigationState} from '@react-navigation/native';
const navigationState = useNavigationState(state => state);
React.useEffect(() => {
// run some code every time `location` change.
}, [navigationState]);
There is a problem with this approach, if you try to use this outside Navigation root component
, an error will be thrown: couldn't find a navigation object.Is your component inside a screen in a navigator?
This is obvious because you are trying to access the react-navigation
state out of its context.
What if I'm creating a component that encloses all the other components of my application including navigation component? something like:
AppStateProvider
type AppStateProviderProps = {
children: React.ReactNode;
};
// some context that u want to pass to componet tree
export const AppStateContext = createContext();
function AppStateProvider({children}: AppStateProviderProps) {
const navigationState = useNavigationState(state => state);
React.useEffect(() => {
// run code every time navigation state change
}, [navigationState]);
return (
<AppStateContext.Provider value={}>
{children}
</AppStateContext.Provider>
);
}
App.tsx - my root component
function App(): JSX.Element{
return (
{/* this will throw the error mentioned above */}
<AppStateProvider>
<NavigationContainer>
{/* ...my navigators and screens */}
</NavigationContainer>
</AppStateProvider>
);
}
Is there a more generic solution outside of navigation
something that I can use in any situation withouth depend of react-navigation
.
Update 2
I want to achieve is to track the global aplication state, i.e if there is any error
or is loading
any resouce, display a loading screen or an error screen respectively. To achieve this, I'm trying to track when the user change of screen and check if there is any error or if any resource is been loading, to show the current screen or loading/error screen.
if there are no errors or if a resource is not being loaded when the screen changes, the application state will be set to undefined
and the screen to which the user is navigating will be displayed.
What I do in react
// Root component App.tsx
import { BrowserRouter } from "react-router-dom";
const App: React.FC = () => {
return (
<BrowserRouter basename={APP_MOUNT_URI}>
{* This pass the current app state to all components, and its updated every time location change *}
<AppStateProvider>
{* other componets *}
</AppStateProvider>
</BrowserRouter>
);
};
// Aplication state with context AppStateProvider.tsx
import React from "react";
import useRouter from "use-react-router";
type AppStateProviderProps = {
children: React.ReactNode;
};
function reduceAppState(
prevState,
action,
) {
switch (action.type) {
case 'displayError':
return displayError(
prevState,
action.payload.error,
action.payload.errorId,
);
case 'displayLoader':
return displayLoader(prevState, action.payload.value);
default:
return prevState;
}
}
const initialAppState: AppStateType = {
error: null,
loading: false,
};
// some context that u want to pass to componet tree
export const AppStateContext = createContext();
function AppStateProvider({children}: AppStateProviderProps) {
const {location} = useRouter();
const stateAndDispatch = React.useReducer(appStateReducer, initialAppState);
const [state, dispatch] = stateAndDispatch;
React.useEffect(() => {
// if there is no error in the application, when you change the
// route, the status will be undefined and the current screen will
// be displayed.
if (state.error) {
dispatch({
payload: {
error: undefined,
},
type: 'displayError',
});
}
}, [location]);
return (
<AppStateContext.Provider value={}>
{children}
</AppStateContext.Provider>
);
}