在初始加载期间反应本机保护经过身份验证的屏幕

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(避免糟糕的用户体验)。