javascript - 如何在 React Navigation 5 身份验证流程中实现 firebase 身份验证?

标签 javascript react-native

我正在尝试在我的应用程序中添加用于注册、登录和注销的 firebase 身份验证。我遵循了 react-navigation 网站上的代码结构。这是上下文文件...

import React from 'react';

export const AuthContext = React.createContext();

这是我的应用程序文件。对于登录,我使用的是电子邮件和密码。对于注册,我想添加电子邮件、用户名和密码。

import React, { useContext, useState, useReducer, useEffect, useMemo } from 'react';
import { Home, Profile, Settings, Chat, Phone } from "./src/screens";
import {
  NavigationContainer,
  DefaultTheme as NavigationDefaultTheme,
  DarkTheme as NavigationDarkTheme
} from '@react-navigation/native';
import {
  Provider as PaperProvider,
  DefaultTheme as PaperDefaultTheme,
  DarkTheme as PaperDarkTheme
} from 'react-native-paper';
import { createDrawerNavigator } from '@react-navigation/drawer';
import AsyncStorage from '@react-native-community/async-storage';
import Fire from './src/api/Fire';
import firebase from 'firebase';

import AppTabs from "./src/stacks/AppTabs";
import AuthStack from "./src/stacks/AuthStack";
import { DrawerContent } from "./src/screens/DrawerContent";
import Spinner from "./src/components/Spinner";
import { AuthContext } from './src/components/Context';


const Drawer = createDrawerNavigator();


export default function App() {
  const [isDarkTheme, setIsDarkTheme] = useState(false);

  {/* Themes */ }
  const CustomDefaultTheme = {
    ...NavigationDefaultTheme,
    ...PaperDefaultTheme,
    colors: {
      ...NavigationDefaultTheme.colors,
      ...PaperDefaultTheme.colors,
      background: '#ffffff',
      text: '#333333'
    }
  }

  const CustomDarkTheme = {
    ...NavigationDarkTheme,
    ...PaperDarkTheme,
    colors: {
      ...NavigationDarkTheme.colors,
      ...PaperDarkTheme.colors,
      background: '#333333',
      text: '#ffffff'
    }
  }

  const theme = isDarkTheme ? CustomDarkTheme : CustomDefaultTheme;

  const [state, dispatch] = useReducer(
    (prevState, action) => {
      switch (action.type) {
        case 'RESTORE_TOKEN':
          return {
            ...prevState,
            userToken: action.token,
            isLoading: false,
          };
        case 'SIGN_IN':
          return {
            ...prevState,
            email: action.id,
            isSignout: false,
            userToken: action.token,
            isLoading: false,
          };
        case 'SIGN_OUT':
          return {
            ...prevState,
            email: null,
            isSignout: true,
            userToken: null,
            isLoading: false,
          };
        case 'REGISTER':
          return {
            ...prevState,
            email: action.id,
            isLoading: false,
            userToken: action.token,
          };
      }
    },
    {
      isLoading: true,
      isSignout: false,
      email: null,
      userToken: null,
    }
  );


  const authContext = useMemo(() => ({
    signIn: async data => {
      // In a production app, we need to send some data (usually username, password) to server and get a token
      // We will also need to handle errors if sign in failed
      // After getting token, we need to persist the token using `AsyncStorage`
      // In the example, we'll use a dummy token

      dispatch({ type: 'SIGN_IN', id: email, token: userToken });
    },
    signOut: async data => {
      dispatch({ type: 'SIGN_OUT' })
    },
    signUp: async data => {
      // In a production app, we need to send user data to the server and get a token
      // We will also need to handle errors if sign up failed
      // After getting token, we need to persist the token using `AsyncStorage`
      // In the example, we'll use a dummy token

      dispatch({ type: 'REGISTER', id: email, token: userToken });
    },
    toggleTheme: () => {
      setIsDarkTheme(isDarkTheme => !isDarkTheme);
    }
  }),
    []
  );


  useEffect(() => {
    setTimeout(async () => {
      let userToken;
      userToken = null;
      try {
        userToken = await AsyncStorage.getItem('userToken');
      } catch (e) {
        console.log(e);
      }
      dispatch({ type: 'RESTORE_TOKEN', token: userToken });
    }, 1000);
  }, []);

  if (state.isLoading) {
    return (
      <Spinner />
    );
  }

  return (
    <PaperProvider theme={theme}>
      <AuthContext.Provider value={authContext}>
        <NavigationContainer theme={theme}>
          {state.userToken !== null ? (
            <Drawer.Navigator drawerContent={props => <DrawerContent {...props} />} >
              <Drawer.Screen name="HomeDrawer" component={AppTabs} />
              <Drawer.Screen name="ProfileDrawer" component={Profile} />
              <Drawer.Screen name="SettingsDrawer" component={Settings} />
              <Drawer.Screen name="PhoneDrawer" component={Phone} />
            </Drawer.Navigator>
          )
            :
            <AuthStack />
          }
        </NavigationContainer>
      </AuthContext.Provider>
    </PaperProvider>
  )
}

Firebase API key 已在此导入文件中初始化。

import Fire from './src/api/Fire';

我看到一些代码将 FirebaseAuth 包装在 NavigationContainer 周围,难道 AuthContext.Provider 不会做同样的事情吗?

我正在使用 useContext 在各自的屏幕上调用 SingInSignUp,就像这样

const { signUp } = useContext(AuthContext); // signup screen
const { signIn } = useContext(AuthContext); // signin screen

然后在 onPress 函数中使用它们,例如 onPress={()=>{signUp(email, username, paswword)}}

最佳答案

我找到了这个链接,知道该怎么做:use-auth

对于使用Reducer的钩子(Hook)

const [state, dispatch] = useReducer(
    (prevState, action) => {
      switch (action.type) {
        case 'RESTORE_TOKEN':
          return {
            ...prevState,
            user: action.payload.user,
            userToken: action.token,
            isLoading: false,
          };
        case 'SIGN_IN':
          return {
            ...prevState,
            user: action.payload.user,
            isSignout: false,
            userToken: action.token,
            isLoading: false,
          };
        case 'SIGN_OUT':
          return {
            ...prevState,
            user: null,
            isSignout: true,
            userToken: null,
            isLoading: false,
          };
        case 'REGISTER':
          return {
            ...prevState,
            isLoading: false,
          };
        default:
          throw new Error(`No case for type ${action.type} found.`)
      }
    },
    {
      isLoading: true,
      isSignout: false,
      userToken: null,
      user: null,
    }
  );

授权上下文

function getRef() {
    return firebase.database().ref();
  }

const authContext = useMemo(() => ({
    signIn: async (email, password, user) => {
      await firebase.auth().signInWithEmailAndPassword(email.trim(), password)
        .then(() => {
          state.isLoading = false
        })
        .catch(function (error) {
          // Handle Errors here.
          const errorCode = error.code
          const errorMessage = error.message
          alert(errorMessage)
          state.isLoading = false
        })
      dispatch({
        type: 'SIGN_IN',
        payload: {
          user: firebase.auth().currentUser,
        },
      });
    },

    signOut: async () => {
      firebase.auth().signOut()
        .then(function () {
          // Sign-out successful.
          state.isLoading = false
        })
        .catch(function (error) {
          // An error happened.
          state.isLoading = false
        })
      dispatch({ type: 'SIGN_OUT' })
    },

    signUp: async (email, password, avatar, displayName, phoneNumber, about) => {
      try {
        await firebase.auth().createUserWithEmailAndPassword(email.trim(), password)
          .then((userInfo) => {
            userInfo.user.updateProfile({
              displayName: displayName,
              photoURL: avatar,
              phoneNumber: phoneNumber,
            })
            console.log(userInfo);
          })
          .then(() => {
            firebase.auth().onAuthStateChanged(user => {
              getRef().child('users')
                .push({
                  avatar: avatar,
                  email: email,
                  name: displayName,
                  phoneNumber: phoneNumber,
                  aboutMe: about,
                  uid: user.uid
                })
            })
          })
      }
      catch (error) {
        alert(error);
      }

      dispatch({
        type: 'REGISTER'
      })
    },

    toggleTheme: () => {
      setIsDarkTheme(isDarkTheme => !isDarkTheme);
    }
  }),
    []
  );

使用效果 Hook

useEffect(() => {
    setTimeout(async () => {
      firebase.auth().onAuthStateChanged(function (user) {
        if (user) {
          // User is signed in.
          if (onAuthStateChange.current) {
            onAuthStateChange.current = false
            return
          }
          dispatch({
            type: 'RESTORE_TOKEN',
            payload: {
              user,
            },
          })
        } else {
          // User is signed out.
          dispatch({
            type: 'SIGN_OUT',
            payload: {
              user: null,
            },
          })
        }
      })
    }, 1000);
  }, []); 

返回函数

return (
    <PaperProvider theme={theme}>
      <AuthContext.Provider value={authContext}>
        <NavigationContainer
          theme={theme}
        >
          {state.user !== null ? (
            <Drawer.Navigator drawerContent={props => <DrawerContent {...props} />} >
              <Drawer.Screen name="HomeDrawer" component={AppTabs} />
              <Drawer.Screen name="ProfileDrawer" component={Profile} />
              <Drawer.Screen name="SettingsDrawer" component={Settings} />
              <Drawer.Screen name="PhoneDrawer" component={Phone} />
            </Drawer.Navigator>
          )
            :
            <AuthStack />
          }
        </NavigationContainer>
      </AuthContext.Provider>
    </PaperProvider>
  )

如果您发现使用不当,请指教。否则,这就是目前对我有用的。即使在应用程序重新启动后,我仍然试图在主题更改时保持主题的状态。

关于javascript - 如何在 React Navigation 5 身份验证流程中实现 firebase 身份验证?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63870214/

相关文章:

javascript - 如何将第三方应用程序 apk 集成到我的 android studio 项目中?

react native react native 矢量图标 : How to use font awesome icons

ios - 如何在 React Native 中更改自定义 slider ?

react-native - React Native 抽屉导航自定义页脚

javascript - 未捕获的语法错误 : Unexpected token < , ajax 调用

javascript - 删除不支持的 unicode 字符,否则显示为正方形

javascript - 使用 javascript 检测按下的 Enter 键

JavaScript 正则表达式匹配 2 个单词和一个具有长度限制的空白字符

javascript - 在 React Native 中更新嵌套状态对象?

react-native - 是否可以将 React-Native View 作为 subview 嵌入到原生 android 项目中?