React Native (Expo) - 能够从任何(嵌套的)child 组件更新顶级组件的状态

React Native (Expo) - Ability to update state of top component from any (nested) child component

我正在开发一个 Expo 应用程序,它使用使用 SecureStore 存储的 JWT 进行身份验证。 top-level 组件最初显示登录屏幕,但也会检查 SecureStore 中是否存在 JWT。如果 JWT 存在,应用程序会验证它是否仍然有效,如果存在,应用程序会将用户带到登录页面,用户可以从该页面导航到许多其他页面,这些页面可以获取和显示各种数据。

我正在寻找一种方法来处理过期的 JWT,这样如果用户正在导航到一个页面,该页面试图获取一些数据和 API 响应 returns 例如401 应用应将用户带回登录屏幕。

顶部组件使用此状态来决定要显示的页面

  const [appState, setAppState] = useState(appStates.STARTUP_SETUP);

appState 的有效值为:

  const appStates = {
    STARTUP_SETUP: "StartupSetup", // Initial state, during which the app checks for an existing valid JWT
    SHOW_LOGIN_SCREEN: "ShowLoginScreen", // There is no stored JWT, app shows login screen
    SHOW_SIGNUP_SCREEN: "ShowSignupScreen", // There is no stored JWT, app shows signup screen
    SHOW_SIGNUP_CONFIRMATION_SCREEN: "ShowSignupConfirmationScreen", // There is no stored JWT, user just registered and is prompted to check their email for verification
    USER_LOGGED_IN: "UserLoggedIn", // user logged in, JWT is stored
  }

该组件按以下方式使用 appState

  if (appState === appStates.USER_LOGGED_IN) {
    comp = <Landing onLogout={logUserOut} />;
  } else if (appState === appStates.SHOW_LOGIN_SCREEN) {
    comp = <Login onSuccessfulLogin={updateUser} onSignup={() => setAppState(appStates.SHOW_SIGNUP_SCREEN)} />;
  } else if (appState === appStates.SHOW_SIGNUP_SCREEN) {
    comp = <Signup onSuccessfulSignup={() => setAppState(appStates.SHOW_SIGNUP_CONFIRMATION_SCREEN)} onLogin={() => setAppState(appStates.SHOW_LOGIN_SCREEN)} />;
  } else if (appState === appStates.SHOW_SIGNUP_CONFIRMATION_SCREEN) {
    comp = <SignupConfirmation onLogin={() => setAppState(appStates.SHOW_LOGIN_SCREEN)} />
  }

Landing 拥有自己的 child 组件树。

我基本上是在寻找一种能够做到的方法

setAppState(appStates.SHOW_LOGIN_SCREEN)

在我的应用中的任何位置。

一种可能性是将该挂钩从顶部组件传递到 Landing 以及每个 child ,但我觉得应该有更简单的方法。

编辑 - 解决方案

在我的顶级组件中,我创建了一个删除令牌并将 appState 设置为 SHOW_LOGIN_SCREEN

的方法
  const deleteStoredToken = () => {
    deleteToken();
    setAppState(appStates.SHOW_LOGIN_SCREEN);
  }
  const appStateValue = { deleteStoredToken };

然后我创建了一个上下文(具有默认值)

export const AppContext = React.createContext({
    deleteStoredToken: () => { }
});

我使用此上下文通过提供 appStateValue 作为其值

来包装顶部组件的 children
return (
    ...
    <AppContext.Provider value={appStateValue}>
        {children}
    </AppContext.Provider>
    ...
)

现在在任何 child 组件中我都能做到

const { deleteStoredToken } = useContext(AppContext);

并使用deleteStoredToken()

听起来您正在寻找状态管理解决方案。 React Context 是对此的 low-effort 解决方案 - 请务必阅读文档中的注意事项。

有数十个(如果不是数百个)库提供不同的方法来执行此操作。最流行的可能是 Redux 和 Mobx;这些提供了在组件之间共享复杂状态的专用方法。但是,如果只是针对一个值,而且不会经常更新,一个Context就完全没问题了。