React Native - 如何在不刷新应用程序的情况下实时收听 AsyncStorage 更改(条件屏幕渲染)?

React Native - How can I listen to AsyncStorage Changes in Real Time (Conditional Screens Rendering) without refreshing the app?

我正在尝试使用以下方法在 React Native 中构建身份验证系统:

这是我的系统流程:我只想在 (isAuthenticated == false) 时显示 Sign 屏幕,当它为 true 时我想显示 UserUI Screen

        <Provider store={store}>
          <NavigationContainer>
          <Stack.Navigator
          screenOptions={{
            headerShown:false
          }}
          >
            {isAuthenticated == false ? (
               <>
               <Stack.Screen name="Sign" component={Sign} />
               </>
            ) : (
              <>
              <Stack.Screen name="UserUI" component={Tabs} />
              </>
            )}
          </Stack.Navigator>
        </NavigationContainer>
      </Provider>

(isAuthenticated) 也取决于此函数:

React.useEffect(()=>{  
  const showStorage = async () => {
    try {
      const userObj = await AsyncStorage.getItem('currentStoredUserState')
      if(userObj != null){
        let jsonObject = JSON.parse(userObj)
        setIsAuthenticated(jsonObject.state)
      }else{
        setIsAuthenticated(false)
      }
      keys()
    }catch(err){
      console.log(err)
    }
 }
  showStorage()
 },[itemsLength])

但问题是当用户进行身份验证时 (isAuthenticated) 不会更改为 true,我必须手动刷新应用程序 - 在 AsyncStorage 不为空后 - 才能访问“UserUI”屏幕。

如您所见,我将 (itemsLength) 设置为依赖项,但效果不佳:

const [itemsLength,setItemLength] = useState(0)
const keys = async () => {
    let key = await AsyncStorage.getAllKeys()
    setItemLength(key.length)
}

上面的都在里面App.js

签到行动:

const setAuthStorage = async (data) => {
    try {
        let authState = { state: true,currentStoredUser_ : data }
        await AsyncStorage.setItem('currentStoredUserState', JSON.stringify(authState))
    } catch (err) {
        console.log(err)
    }
}

export const sign_in = (userSignData) => (dispatch) => {
    auth.signInWithEmailAndPassword(userSignData.email, userSignData.password)
        .then(userCredential => {
            const user = userCredential.user
            db.collection('users').doc(auth.currentUser.uid.toString()).get()
                .then(doc => {
                    if (doc.exists) {
                        setAuthStorage(doc.data())
                        dispatch({
                            type: SIGN_IN,
                            payload: {
                                authenticated: true,
                                currentUser: user,
                            }
                        })
                    }
                })
        }).catch(err => {
            console.log(err)
        })
}

在签名屏幕中签名 Hanlder :

const signHandler = () => {
        if (isSignedUp) {
            dispatch(sign_in(userSignData))
        } else {
            dispatch(sign_up(userSignData))
        }
    }


已解决,但还不够完美,-

谢谢你们的帮助我真的很感激,你们让我使用 redux state 改变渲染条件但是我无法访问外部的状态,为了这个任务我必须订阅在 App.js 组件 中存储更改,当我们说订阅 == 监听每个状态更改时,这对应用程序性能不利:

store.subscribe(()=>{
  /*
    I combined two states: rootReducer (for user details) and postReducer(for posts changes)
  */

// making sure it's rootReducer state
if(store.getState().hasOwnProperty('rootReducer')){
  //when user sign in or sign up rootReducer.authenticated will change from false to true
  
  if(store.getState().rootReducer.authenticated == true){
      // change the rendering condition
      setIsAuthenticated(true)
    }else{
      setIsAuthenticated(false)
    }
  }
})

我还在操作中做了一些更改,以便仅在存储数据后更改状态

const setAuthStorage = async (data) => {
    try {
        let authState = { state: true,currentStoredUser_ : data }
        // to provide promise chaining this function will store & return a promise
        return await AsyncStorage.setItem('currentStoredUserState', JSON.stringify(authState))
    } catch (err) {
        console.log(err)
    }
}


export const sign_in = (userSignData) => (dispatch) => {
    auth.signInWithEmailAndPassword(userSignData.email, userSignData.password)
        .then(userCredential => {
            const user = userCredential.user
            db.collection('users').doc(auth.currentUser.uid.toString()).get()
                .then(doc => {
                    if (doc.exists) {
                        setAuthStorage(doc.data())
                        .then(()=>{
                            dispatch({
                                type: SIGN_IN,
                                payload: {
                                    authenticated: true,
                                    currentUser: user,
                                }
                            })
                        })
                    }
                })
        }).catch(err => {
            console.log(err)
        })
}

如果找到更好的解决方案,我会与大家分享

你应该在 redux 状态下显示有条件的路由,然后它会在登录时自动更改路由。

据我了解,您要实现的目标是,

  1. 未登录时使用登录屏幕,登录时使用用户屏幕。
  2. 一旦用户登录保存数据并保持用户登录。

怎么和你的最相似:

  1. 在 redux 中存储用户数据。(我想你已经完成了。)。
  2. 使用该 redux 状态 select 哪一组屏幕可用(您使用了异步存储,更新时不会重新呈现)。
  3. 将用户数据保存在 redux 中。可以为此使用 redux-persist。(为下次用户打开应用程序保存用户信息)。

编辑 添加代码以使问题解决方案更好

App.js 文件

import AppNavigation from './AppNavigation'
...
<Provider store={store}>
  <AppNavigation/>
</Provider>

AppNavigation.js

import {useSelector} from 'react-redux'
...
const AppNavigation =()=>{
  const isAuthenticated = useSelector(state=>state.authenticated===true);
  return(
    <NavigationContainer>
      <Stack.Navigator
      screenOptions={{
        headerShown:false
      }}
      >
        {isAuthenticated == false ? (
           <>
           <Stack.Screen name="Sign" component={Sign} />
           </>
        ) : (
          <>
          <Stack.Screen name="UserUI" component={Tabs} />
          </>
        )}
      </Stack.Navigator>
    </NavigationContainer>
  )
}