有没有一种方法可以在每次路线/屏幕发生变化时通过反应本机导航来运行代码?

Cri*_*rez 5 reactjs react-native react-native-navigation

在 React 中,每次更改时我都可以location使用use-react-router执行代码,这使我可以访问historylocationmatch属性。我所做的是使用 auseEffect并在每次location属性更改时运行代码,如下所示:

import useRouter from "use-react-router";

const { location } = useRouter();

React.useEffect(() => {
  // run some code every time `location` change.
}, [location]);
Run Code Online (Sandbox Code Playgroud)

有没有办法通过react-native中的react-navigation来实现相同的行为?

更新 1 - 使用 useNavigationState 挂钩的临时解决方案

我试图检查导航状态是否发生变化并在发生变化时运行代码,为了实现这一点,我使用了useNavigationStatereact-navigation

import {useNavigationState} from '@react-navigation/native';

const navigationState = useNavigationState(state => state);

React.useEffect(() => {
  // run some code every time `location` change.
}, [navigationState]);
Run Code Online (Sandbox Code Playgroud)

这种方法有一个问题,如果您尝试在外部使用此方法Navigation root component,则会抛出错误:couldn't find a navigation object.Is your component inside a screen in a navigator?这很明显,因为您试图react-navigation在上下文之外访问状态。

如果我正在创建一个包含应用程序的所有其他组件(包括导航组件)的组件,该怎么办?就像是:

应用程序状态提供者

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>
  );
}
Run Code Online (Sandbox Code Playgroud)

App.tsx - 我的根组件

function App(): JSX.Element{
  return (    
    {/* this will throw the error mentioned above */}
    <AppStateProvider> 
      <NavigationContainer>
        {/* ...my navigators and screens */} 
      </NavigationContainer>
    </AppStateProvider>
  );
}
Run Code Online (Sandbox Code Playgroud)

navigation除了我可以在任何情况下使用而无需依赖的东西之外,是否有更通用的解决方案react-navigation

更新2

我想要实现的是跟踪全局应用程序状态,即如果有任何error资源loading,则分别显示加载屏幕或错误屏幕。为了实现这一目标,我试图跟踪用户何时更改屏幕并检查是否有任何错误或是否正在加载任何资源,以显示当前屏幕或加载/错误屏幕。

如果没有错误或者屏幕更改时未加载资源,则应用程序状态将设置为 ,undefined并且将显示用户导航到的屏幕。

我在反应中做什么

// 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>
  );
};
Run Code Online (Sandbox Code Playgroud)
// 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>
  );
}

Run Code Online (Sandbox Code Playgroud)

Dal*_*ney 1

在更新 2 之前:

一种好的方法可能是在附加到主容器的导航引用上使用事件侦听器。请参阅此处的文档:https ://reactnavigation.org/docs/navigation-events/

navigationRef 可以在大多数地方导出和访问。如果您想从导航容器的父级检查导航状态,它可能如下所示:

import { useEffect } from 'react'
import {
    createNavigationContainerRef,
    EventListenerCallback,
    NavigationContainerEventMap,
    NavigationContainer,
} from '@react-navigation/native'
import { createNativeStackNavigator } from '@react-navigation/native-stack'

type StackParamList = {
    Screen1: undefined
    Screen2: undefined
}
const Screen1 = () => <></>
const Screen2 = () => <></>

const navigationRef = createNavigationContainerRef<StackParamList>()
const Stack = createNativeStackNavigator<StackParamList>()

const Navigation = () => (
    <NavigationContainer ref={navigationRef}>
        <Stack.Navigator>
            <Stack.Screen name="Screen1" component={Screen1} />
            <Stack.Screen name="Screen2" component={Screen2} />
        </Stack.Navigator>
    </NavigationContainer>
)

const App = () => {
    useEffect(() => {
        const handleStateChange: EventListenerCallback<
            NavigationContainerEventMap,
            'state'
        > = (event) => {
            const state = event.data.state

            // do stuff with state
            console.log(state)
        }

        // Add listener on mount
        navigationRef.addListener('state', handleStateChange)

        // Remove listener on unmount
        return () => navigationRef.removeListener('state', handleStateChange)
    }, [navigationRef])

    return <Navigation />
}

export default App
Run Code Online (Sandbox Code Playgroud)