【问题标题】:Why is Redux throwing a null error when I try to log out a user with Firebase?当我尝试使用 Firebase 注销用户时,为什么 Redux 会抛出 null 错误?
【发布时间】:2021-05-17 22:49:21
【问题描述】:

为了快速了解我的应用程序,您可以在该应用程序中进行测试、给自己评分和记录您的分数。它使用 Firebase Auth 和 Firestore 和 Redux 来帮助完成这一切。我在退出用户帐户时遇到问题。

redux/actions/index.js:

import { USER_STATE_CHANGE, CLEAR_DATA, USER_TESTS_COMPLETED_STATE_CHANGE } from '../constants/index'
import firebase from 'firebase'

export function clearData() {
    return ((dispatch) => {
        dispatch({type: CLEAR_DATA})
    })
}

export function fetchUser(){
    return((dispatch) => {
        firebase.firestore().collection("users").doc(firebase.auth().currentUser.uid).get().then((snapshot) => {
            if(snapshot.exists){
                dispatch({type : USER_STATE_CHANGE, currentUser: snapshot.data()})
            } else {
                console.log("user data does not exist");
            }
        })
    })
}

export function fetchUserCompletedTests(){
    return((dispatch) => {
        firebase.firestore().collection("users").doc(firebase.auth().currentUser.uid).collection("scores").orderBy("testScore", "desc").get().then((snapshot) => {
            let completedTests = snapshot.docs.map(doc => {
                const data = doc.data();
                const id = doc.id;
                return { id, ...data }
            })
            dispatch({ type: USER_TESTS_COMPLETED_STATE_CHANGE, completedTests})
        })
    })
}

redux/constants/index.js:

export const USER_STATE_CHANGE = 'USER_STATE_CHANGE'
export const USER_TESTS_COMPLETED_STATE_CHANGE = 'USER_TESTS_COMPLETED_STATE_CHANGE'
export const CLEAR_DATA = 'CLEAR_DATA'

redux/reducers/index.js:

import { combineReducers } from 'redux'
import { user } from './user'

const Reducers = combineReducers({
    userState: user
})

export default Reducers

redux/reducers/user.js:

import { USER_STATE_CHANGE, CLEAR_DATA, USER_TESTS_COMPLETED_STATE_CHANGE } from "../constants"

const initialState = {
    currentUser: null,
    completedTests: [], 
}

export const user = (state = initialState, action) => {
    switch (action.type) {
        case USER_STATE_CHANGE:
            return {
                ...state,
                currentUser: action.currentUser
            }
        case CLEAR_DATA:
            return initialState
        case USER_TESTS_COMPLETED_STATE_CHANGE:
            return {
                ...state,
                completedTests: action.completedTests
            }
        default:
            return state;
    }
}

对于已登录的用户,应用程序在这个部分重新创建的Main.js 中启动:

import React, { Component } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { fetchUser, clearData, fetchUserCompletedTests } from '../redux/actions/index'

export class Main extends Component {
    componentDidMount() {
        this.props.clearData();
        this.props.fetchUser();
        this.props.fetchUserCompletedTests();
    }
    render() {
        // bottom tab navigation buttons - between Home.js, Study.js, and Profile.js
    }
}
 
const mapStateToProps = (store) => ({
    currentUser: store.userState.currentUser
})
const mapDispatchProps = (dispatch) => bindActionCreators({fetchUser, clearData, fetchUserCompletedTests}, dispatch)
export default connect(mapStateToProps, mapDispatchProps)(Main);

如果对您有帮助,我的 Profile.js 文件中有此代码可以让用户退出:

function Profile(props) {

import { connect } from 'react-redux'
    import { bindActionCreators } from 'redux'
    import { fetchUser } from '../../redux/actions/index'

    ...

    const onLogout = () => {
            firebase.auth().signOut().then(() => {
    
            }).catch((error) => {
                console.log(error);
            })
        }

    ...
}

    const mapStateToProps = (store) => ({
        currentUser: store.userState.currentUser,
    })
    const mapDispatchProps = (dispatch) => bindActionCreators({fetchUser}, dispatch)
    export default connect(mapStateToProps, mapDispatchProps)(Profile);

问题开始于我将这段代码实现到Home.js

import React, { useState, useEffect } from 'react'
import { View, Text, FlatList } from 'react-native'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { fetchUser, fetchUserCompletedTests } from '../../redux/actions/index'

function Home(props) {
    
    useEffect(() => {
        props.fetchUser();
    }, [props.currentUser] )
    
    useEffect(() => {
        props.fetchUserCompletedTests();
    }, [props.completedTests] )
    
    const { currentUser } = props;

    return (
        <View style={{flex: 1}}>
                <Text>Welcome back, {currentUser.name}!</Text>
                 // FlatList that shows the data stored from calling fetchUserCompletedTests()
        </View>
    )
}

const mapStateToProps = (store) => ({
    currentUser: store.userState.currentUser,
    completedTests: store.userState.completedTests
})
const mapDispatchProps = (dispatch) => bindActionCreators({fetchUser, fetchUserCompletedTests}, dispatch)
export default connect(mapStateToProps, mapDispatchProps)(Home);

(我在Home.js 中再次使用了fetchUser()fetchUserCompletedTests(),以便在用户导航到家时反映应用程序中的任何本地更改 - 例如用户进行测试,然后是与该测试有关的数据存储在 Firebase 中) 当我尝试注销时出现问题,返回此错误:

null 不是对象(评估'_firebase.default.auth().currentUser.uid')

如果我在收到错误后重新加载应用程序,则会退出。 在我使用 Home.js 中的 useEffect 来获取用户每次导航到它的所有数据之前,此错误并未出现。当我尝试注销时,我在设置中做错了什么导致此错误?

【问题讨论】:

    标签: javascript firebase react-native redux react-redux


    【解决方案1】:

    只需查看您的代码,您只需取消引用一次 uid,此处:firebase.firestore().collection("users").doc(firebase.auth().currentUser.uid).get().then((snapshot) =&gt; {

    所以我想你取消引用 firebase.auth() 的这一行可能是你得到空值的地方。

    我的意思是,在你调用 firebase.auth().signOut() 之后,你希望 props.currentUser 发生变化吗?此外,如果 fetchUser 会在注销后触发,那么如果不是 null,您希望 firebase.auth().currentUser.uid 实际上是什么?

    您的目标是在注销后调用 fetchUser 吗?如果是这样,那么也许你的 props.currentUser 应该有一个 if() 块,以确保它不会在 props.currentUser 为 null 时调用 fetchUser()。

    如果你能把这一系列事件整理出来,我想你会准备好的。

    【讨论】:

    • 嘿,这让我开始思考为什么我把它放在首位,所以我在redux/reducers/user.js 中进行了这个更改:const initialState = { currentUser: '', completedTests: [] } 到目前为止,我找不到任何问题改变,它似乎工作得很好。
    • 可能是一种解决方法而不是修复方法。当 currentUser 不为空时,它通常是一个字符串吗?如果它是一个对象,你最好只是从一致性的角度将它作为 null 处理。但正如您发现将其设为非 null 或安全测试其可空性是同一枚硬币的两个方面!
    • currentUser 是一个相当大的 Firebase 对象,它只是有关用户的所有相关数据,例如 uid、注册时间、电子邮件地址等。currentUser.uid 将是一个字符串,不过.我同意从一致性的角度来看,创建一个对象 null 而不是一个空字符串会更有意义,但这是有效的,所以我真的不能抱怨它。也许我还没有完全理解 Redux。谁知道呢。
    • 很高兴您找到了解决方案,感谢您的赞誉并接受!最后我想说的是,它并不是真正的 redux 功能。 Redux 只是一个大型对象存储,您可以将东西放入其中,并且它周围有一些护栏来保持存储的健全。真正的问题是对象一致性。例如,当对象为空/空时,您如何处理它。如果它应该是一个对象,那么您通常不会通过将其设为空字符串来检查它是否为空。随意研究有关这些想法的多种意见!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-21
    • 1970-01-01
    相关资源
    最近更新 更多