【问题标题】:React-Native - Navigation - AWS Amplify Auth - Can't perform a React state update on an unmounted componentReact-Native - 导航 - AWS Amplify Auth - 无法对未安装的组件执行 React 状态更新
【发布时间】:2022-07-05 18:19:25
【问题描述】:

在加载我的 react-native/expo 应用程序时,我无法弄清楚如何消除此警告。我正在使用 AWS Amplify 的身份验证和 @react-navigation 底部选项卡以及授权屏幕和登录屏幕的堆栈。我可以毫无问题地登录和注销,身份验证和应用程序堆栈已被替换并在这些操作上正确显示,所以看起来我对 Hub.listen('auth', (data) => 的调用正在运行。

但是...当我第一次使用 expo start 打开项目并且用户已经通过身份验证/登录时,我收到此错误: Warning: Can't perform a React state update on an unmounted component.

如果我刷新页面,没有错误!或者如果我退出,然后再次登录,没有错误!它只发生在应用程序从 expo 开始的初始加载中。

这是我的 navigation.js 组件代码:

import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Ionicons } from '@expo/vector-icons';

import { useDispatch } from 'react-redux'
import { updateUser } from '../features/user/userSlice'

import Welcome from '../screens/Welcome';
import SignIn from '../screens/SignIn';

import Home from '../screens/Home';
import Profile from '../screens/Profile';
import Colors from '../screens/Colors';
import Loading from '../screens/Loading';

import { Auth, Hub } from 'aws-amplify';

const AuthTabs = createBottomTabNavigator();
const AuthTabsScreen = () => (
  <AuthTabs.Navigator>
    <AuthTabs.Screen
      name="Welcome"
      component={Welcome}
      options={{
        tabBarIcon: (props) => (
          <Ionicons name="home" size={props.size} color={props.color} />
        ),
      }}
    />
    <AuthTabs.Screen
      name="Sign In"
      component={SignIn}
      options={{
        tabBarIcon: (props) => (
          <Ionicons name="home" size={props.size} color={props.color} />
        ),
      }}
    />
  </AuthTabs.Navigator>
);

const AppStack = createStackNavigator();
const AppStackScreen = () => (
  <AppStack.Navigator
    screenOptions={{ animationEnabled: false, presentation: "modal", headerShown: true }}
  >
    <AppStack.Screen
      name="Home"
      component={Home}
      options={{
        headerTitle: 'Home',
      }}
    />
    <AppStack.Screen name="Red"
      options={{
        headerTitle: 'Red Food',
      }}
    >{props => (<Colors {...props} color={"red"} />)}</AppStack.Screen>
    <AppStack.Screen name="Orange">{props => (<Colors {...props} color={"orange"} />)}</AppStack.Screen>
    <AppStack.Screen name="Yellow">{props => (<Colors {...props} color={"yellow"} />)}</AppStack.Screen>
    <AppStack.Screen name="Green">{props => (<Colors {...props} color={"green"} />)}</AppStack.Screen>
    <AppStack.Screen name="Purple">{props => (<Colors {...props} color={"purple"} />)}</AppStack.Screen>
    <AppStack.Screen name="Blue"
      options={{
        headerTitle: 'Water',
      }}
    >
      {props => (<Colors {...props} color={"blue"} />)}
    </AppStack.Screen>
  </AppStack.Navigator>
);

const AppTabs = createBottomTabNavigator();
const AppTabsScreen = () => (
  <AppTabs.Navigator
    screenOptions={{ animationEnabled: false, presentation: "modal", headerShown: false }}>
    <AppTabs.Screen
      name="AppHome"
      component={AppStackScreen}
      options={{
        tabBarIcon: (props) => (
          <Ionicons name="home" size={props.size} color={props.color} />
        ),
      }}
    />
    <AppTabs.Screen
      name="My Stuff"
      component={Profile}
      options={{
        tabBarIcon: (props) => (
          <Ionicons
            name="checkmark-circle-outline"
            size={props.size}
            color={props.color}
          />
        ),
      }}
    />
  </AppTabs.Navigator>
);

const RootStack = createStackNavigator();
const RootStackScreen = () => {
  const dispatch = useDispatch()
  const [isLoading, setIsLoading] = React.useState(true);
  const [user, setUser] = React.useState(null);

  React.useEffect(() => {

    Hub.listen('auth', (data) => {
      const { payload } = data
      //console.log('A new auth event has happened: ', payload)
      if (payload.event === 'signIn') {
        console.log('A user has signed in!', payload.data.username)
        setUser(payload.data.username)
        dispatch(updateUser(payload.data.username))
      }
      if (payload.event === 'signOut') {
        //console.log('A user has signed out!')
        setUser()
      }
    })

    return () => {
      Hub.remove('auth')
    }
  }, []);

  React.useEffect(() => {    
    setIsLoading(!isLoading);
    checkUser()
  }, []);

  async function checkUser() {
    try {
      const thisUser = await Auth.currentAuthenticatedUser()
      //console.log('thisUser', thisUser.username)      
      if (thisUser) {
        setUser(thisUser.username)
        dispatch(updateUser(thisUser.username))
      }
    } catch (err) {
      //console.log(' User is not signed in', err)
      setUser()
    }
  }

  return (
    <RootStack.Navigator
      screenOptions={{ animationEnabled: false, presentation: "modal", headerShown: false }}
    >
      {isLoading ? (
        <RootStack.Screen name="Loading" component={Loading} />
      ) : user ? (
        <RootStack.Screen name="AppTabsScreen" component={AppTabsScreen} />
      ) : (
        <RootStack.Screen name="AuthTabsScreen" component={AuthTabsScreen} />
      )}
    </RootStack.Navigator>
  );
};

export default () => {
  return (
    <NavigationContainer>
      <RootStackScreen />
    </NavigationContainer>
  );
};

我会忽略这个警告吗?它会影响我的 ios 构建/应用程序吗?任何帮助将不胜感激。

【问题讨论】:

标签: react-native authentication react-navigation aws-amplify


【解决方案1】:

我对此的理解是,每次 Amplify Auth API 在组件已卸载后响应时都会出现警告。 This 博客帮我修复了它。

假设您有一个屏幕,您将其渲染包裹在 withAuthenticator() HOC 中,其中包含一个退出按钮。

const signOut = async () => {
  try{
    await Auth.signOut({global: true})
    navigation.navigate("SplashScreen")
  }
  catch(error){
  ...
  }
}

一个按钮按如下方式连接到它:

<TouchableOpacity onPress={signOut}>
    <Text>Sign Out</Text>
</TouchableOpacity>

如果您将此注销功能编码为如上所述导航回初始屏幕,则注销和后续登录会导致问题中的错误。

因此,

  1. 添加一个局部变量来跟踪用户当前是否按照 Amplify Auth 登录。如果是这样,则应将此变量设置为 true。

`

const [isLogged, setIsLogged]=useState(false)
    const updateLoggedStatus = async () => {
      const userName = await getCurrentUserName
      let newLoggedStatus = false
      if(userName){
        newLoggedStatus = true
      }
      setIsLogged(newLoggedStatus)
    }

getCurrentUserName 可以添加到导出的辅助类中(为了可重用性):

import {Auth} from 'aws-amplify' 

export const getCurrentUserName = () => {
  return new Promise((resolve, reject)=>{
    Auth.currentAuthenticatedUser()
    .then(user=>{
     if(user.userName) resolve(user.userName)
     else resolve(null)
  })
  .catch(err=> resolve(null))
  })
}
  1. 一旦我们有了这个,使用useEffect(),我们可以分别在组件首次挂载和最后卸载时注册和取消注册集线器 (import {Hub} from aws-amplify)。

`

useEffect(() => {
      //only when component first renders
      updateLoggedStatus()
      Hub.listen('auth', updateLoggedStatus)
      return () => {
        Hub.remove('auth') // only when component finally unmounts
      }
    }, [])

【讨论】:

    猜你喜欢
    • 2021-11-30
    • 2020-09-03
    • 2022-01-18
    • 1970-01-01
    • 2019-09-11
    • 2021-08-07
    • 2020-06-14
    • 1970-01-01
    • 2021-07-01
    相关资源
    最近更新 更多