在初始加载期间反应本机保护经过身份验证的屏幕
react native protect authenticated screens during initial loading
我已经使用 firebase
email/pass 身份验证制作了一个应用程序并使用 redux-toolkit
管理状态,一切正常,除了糟糕的用户体验,也就是说,当应用程序打开后,需要一秒钟来确定用户是否登录,为此,我在 redux 中有一个加载状态,这也工作正常,但在初始加载期间,我可以看到我的 LoginScreen 显示首先,然后在一两秒后,如果用户之前登录过,它会显示我的主屏幕,所以我需要提供一个 LoadingScreen 以隐藏该转换,并且只在每次加载操作完成时显示一个特定的屏幕。
我尝试添加本地状态并将其设置为 false,当在 onAuthStateChange
函数内完成所有操作时,它不起作用。
我的问题视频:https://drive.google.com/file/d/1a8Bu1bTqAANsWKDs0e5bjqVdZJWJRcMv/view?usp=sharing
请看一下录音(link以上),以便您正确了解情况!
下面是我的代码,入口文件和redux工具包状态文件:
入口文件:
const EntryPoint = () => {
const dispatch = useDispatch();
const user = useSelector(state => state.auth.user);
const authloading = useSelector(state => state.auth.loading);
useEffect(() => {
onAuthStateChanged(auth, result => {
if (result) {
const path = doc(db, 'users', result.uid);
getDoc(path)
.then(result => {
if (result.exists()) {
const data = result.data();
dispatch(
login({
displayName: data['displayName'],
email: data['email'],
photoURL: data['photoURL'],
phone: data['phone'],
}),
);
}
})
.catch(error => console.log('sign in error: ', error));
} else {
dispatch(logout());
}
dispatch(loading(false));
});
}, []);
if (authloading) return <Loader />;
return (
<Stack.Navigator>
{user ? <Stack.Screen name="Home" component={Home} /> : <Stack.Screen name="SignIn" component={SignIn} />}
</Stack.Navigator>
);
};
Redux 状态:
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
user: null,
loading: true,
}
export const authSlice = createSlice({
name: 'auth',
initialState,
reducers: {
login: (state, action) => {
state.user = action.payload;
},
logout: (state) => {
state.user = null;
},
loading: (state, action) => {
state.loading = action.payload;
},
}
});
我认为发生这种情况是因为您在设置 user
之前设置了 loading = false
这很清楚,因为 user
将在 getDoc
调用之后设置(这会创建那 1 秒或 2 秒的“糟糕的用户体验”)。
Redux dispatch
returns 一个 Promise,因此您可以使用该 Promise 在用户设置后将 loading
设置为 false:
const EntryPoint = () => {
const dispatch = useDispatch();
const user = useSelector(state => state.auth.user);
const authloading = useSelector(state => state.auth.loading);
useEffect(() => {
onAuthStateChanged(auth, result => {
if (result) {
const path = doc(db, 'users', result.uid);
getDoc(path)
.then(result => {
if (result.exists()) {
const data = result.data();
dispatch(
login({
displayName: data['displayName'],
email: data['email'],
photoURL: data['photoURL'],
phone: data['phone'],
}),
)
.then(result => { dispatch(loading(false)); }); //<-- after user is setted, remove loading
}
else dispatch(loading(false)); //<-- if result does not exixsts, remove loading
})
.catch(error => {
dispatch(loading(false)); //<-- in case of error, remove loading
console.log('sign in error: ', error);
});
} else {
dispatch(logout())
.then(result => { dispatch(loading(false)); }); //<-- after logout, remove loading
}
});
}, []);
if (authloading) return <Loader />;
return (
<Stack.Navigator>
{user ? <Stack.Screen name="Home" component={Home} /> : <Stack.Screen name="SignIn" component={SignIn} />}
</Stack.Navigator>
);
};
通过这种方式,您可以 100% 确定仅在设置用户后加载才会设置为 false(避免糟糕的用户体验)。
我已经使用 firebase
email/pass 身份验证制作了一个应用程序并使用 redux-toolkit
管理状态,一切正常,除了糟糕的用户体验,也就是说,当应用程序打开后,需要一秒钟来确定用户是否登录,为此,我在 redux 中有一个加载状态,这也工作正常,但在初始加载期间,我可以看到我的 LoginScreen 显示首先,然后在一两秒后,如果用户之前登录过,它会显示我的主屏幕,所以我需要提供一个 LoadingScreen 以隐藏该转换,并且只在每次加载操作完成时显示一个特定的屏幕。
我尝试添加本地状态并将其设置为 false,当在 onAuthStateChange
函数内完成所有操作时,它不起作用。
我的问题视频:https://drive.google.com/file/d/1a8Bu1bTqAANsWKDs0e5bjqVdZJWJRcMv/view?usp=sharing
请看一下录音(link以上),以便您正确了解情况!
下面是我的代码,入口文件和redux工具包状态文件:
入口文件:
const EntryPoint = () => {
const dispatch = useDispatch();
const user = useSelector(state => state.auth.user);
const authloading = useSelector(state => state.auth.loading);
useEffect(() => {
onAuthStateChanged(auth, result => {
if (result) {
const path = doc(db, 'users', result.uid);
getDoc(path)
.then(result => {
if (result.exists()) {
const data = result.data();
dispatch(
login({
displayName: data['displayName'],
email: data['email'],
photoURL: data['photoURL'],
phone: data['phone'],
}),
);
}
})
.catch(error => console.log('sign in error: ', error));
} else {
dispatch(logout());
}
dispatch(loading(false));
});
}, []);
if (authloading) return <Loader />;
return (
<Stack.Navigator>
{user ? <Stack.Screen name="Home" component={Home} /> : <Stack.Screen name="SignIn" component={SignIn} />}
</Stack.Navigator>
);
};
Redux 状态:
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
user: null,
loading: true,
}
export const authSlice = createSlice({
name: 'auth',
initialState,
reducers: {
login: (state, action) => {
state.user = action.payload;
},
logout: (state) => {
state.user = null;
},
loading: (state, action) => {
state.loading = action.payload;
},
}
});
我认为发生这种情况是因为您在设置 user
之前设置了 loading = false
这很清楚,因为 user
将在 getDoc
调用之后设置(这会创建那 1 秒或 2 秒的“糟糕的用户体验”)。
Redux dispatch
returns 一个 Promise,因此您可以使用该 Promise 在用户设置后将 loading
设置为 false:
const EntryPoint = () => {
const dispatch = useDispatch();
const user = useSelector(state => state.auth.user);
const authloading = useSelector(state => state.auth.loading);
useEffect(() => {
onAuthStateChanged(auth, result => {
if (result) {
const path = doc(db, 'users', result.uid);
getDoc(path)
.then(result => {
if (result.exists()) {
const data = result.data();
dispatch(
login({
displayName: data['displayName'],
email: data['email'],
photoURL: data['photoURL'],
phone: data['phone'],
}),
)
.then(result => { dispatch(loading(false)); }); //<-- after user is setted, remove loading
}
else dispatch(loading(false)); //<-- if result does not exixsts, remove loading
})
.catch(error => {
dispatch(loading(false)); //<-- in case of error, remove loading
console.log('sign in error: ', error);
});
} else {
dispatch(logout())
.then(result => { dispatch(loading(false)); }); //<-- after logout, remove loading
}
});
}, []);
if (authloading) return <Loader />;
return (
<Stack.Navigator>
{user ? <Stack.Screen name="Home" component={Home} /> : <Stack.Screen name="SignIn" component={SignIn} />}
</Stack.Navigator>
);
};
通过这种方式,您可以 100% 确定仅在设置用户后加载才会设置为 false(避免糟糕的用户体验)。