Search code examples
reactjstypescriptreact-nativeredux-toolkit

RTK: useSelector() vs store.getState()


I am working on a project with another developer and we're at an impasse on how the authentication flow should go. We use RTK for state management. In the EntryPoint.tsx, here's my approach:

export default function EntryPoint({ layoutHandler }: EntryPointProps) {

  const { isDarkMode, themeColors } = useContext(ThemeContext);
  const { accessToken } = useSelector((state:RootState) => state.auth)

  return (
    <>
      <StatusBar
        animated
        backgroundColor={themeColors['neutral-00']}
        barStyle={isDarkMode ? 'light-content' : 'dark-content'}
      />

      <View
        onLayout={layoutHandler}
        style={[
          globalStyles.container,
          {
            backgroundColor: themeColors['neutral-00'],
            paddingBottom:
              Platform.OS === 'android' ? styleGuide.layout.spacing.md : 0,
          },
        ]}
      >
        {!accessToken ? <AuthNavigator /> : <MainNavigator />}
      </View>
    </>
  );
}

In this approach, during logout, all that's needed is to remove/destroy accessToken and you'll be sent to the navigator stack that contains screens for authentication (OnBoarding, Login, Verify Login)

Here's my colleague's approach

export default function EntryPoint({ layoutHandler }: EntryPointProps) {
  const { isDarkMode, themeColors } = useContext(ThemeContext);

  const authState = store.getState().auth as AuthStateType;
  const { isOnboarded } = authState;

  return (
    <>
      <StatusBar
        animated
        backgroundColor={themeColors['neutral-00']}
        barStyle={isDarkMode ? 'light-content' : 'dark-content'}
      />

      <View
        onLayout={layoutHandler}
        style={[
          globalStyles.container,
          {
            backgroundColor: themeColors['neutral-00'],
            paddingBottom:
              Platform.OS === 'android' ? styleGuide.layout.spacing.md : 0,
          },
        ]}
      >
        {!isOnboarded ? <OnboardingNavigator /> : <AuthNavigator />}
      </View>
    </>
  );
}

Basically, he's now rearranged the navigator stacks such that OnBoardingNavigator stack contains only Login & AuthNavigator stack. Do note that the AuthNavigator now contains Login screen again and the the MainNavigator. Logout now works in such a way that after accessToken is removed, we navigate back to Login screen.

Reason for his approach is he doesn't want to use useSelector as subscribing to the store is costly and will lead to unknowns and unpredictability.

I seriously disagree with this as I believe the cost of subscription isn't worth refactoring all the navigation stacks which now mixes multiple screens in stacks they do not belong in. Simply using useSelector will make the app React . However, He says reactivity comes at a cost.

What is the best approach/practice in this case?


Solution

  • Redux maintainer here. Please don't use store.getState() in your component like that ever.

    If your store changes in the background, the component will not rerender with a new value. If you write your selector correctly, it will only rerender when the selector value changes.

    So you can optimize here, but should keep the useSelector call.

    const isOnboarded = useSelector((state:RootState) => state.auth.accessToken.isOnboarded)
    

    is a lot better.