React Native - 如何在不刷新应用程序的情况下实时收听 AsyncStorage 更改(条件屏幕渲染)?
React Native - How can I listen to AsyncStorage Changes in Real Time (Conditional Screens Rendering) without refreshing the app?
我正在尝试使用以下方法在 React Native 中构建身份验证系统:
- 反应导航
- 异步存储
- redux
这是我的系统流程:我只想在 (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 状态下显示有条件的路由,然后它会在登录时自动更改路由。
据我了解,您要实现的目标是,
- 未登录时使用登录屏幕,登录时使用用户屏幕。
- 一旦用户登录保存数据并保持用户登录。
怎么和你的最相似:
- 在 redux 中存储用户数据。(我想你已经完成了。)。
- 使用该 redux 状态 select 哪一组屏幕可用(您使用了异步存储,更新时不会重新呈现)。
- 将用户数据保存在 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>
)
}
我正在尝试使用以下方法在 React Native 中构建身份验证系统:
- 反应导航
- 异步存储
- redux
这是我的系统流程:我只想在 (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 状态下显示有条件的路由,然后它会在登录时自动更改路由。
据我了解,您要实现的目标是,
- 未登录时使用登录屏幕,登录时使用用户屏幕。
- 一旦用户登录保存数据并保持用户登录。
怎么和你的最相似:
- 在 redux 中存储用户数据。(我想你已经完成了。)。
- 使用该 redux 状态 select 哪一组屏幕可用(您使用了异步存储,更新时不会重新呈现)。
- 将用户数据保存在 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>
)
}