Redux/Firebase - 在 state 中接收到 uid 后组件未更新

Redux/Firebase - Component not updating after uid is received in state

我正在为我的 Redux 应用程序使用 Firebase 的身份验证 API。我有一个应用程序组件,应该在收到用户的 UID 身份验证时收到通知,并切换向用户显示 header(登录 header 或注销 header)。但是,在用户已经登录并重新访问主路由的情况下,当 UID 存储到应用程序状态时,我似乎无法将我的主应用程序设置为 re-render。

大致流程是这样的:

  1. In routes/index.js: Firebase 的 onAuthStateChanged 观察者 识别用户是否已经登录 路由负载。如果有用户,发送操作来复制他们的 UID 从 Firebase 到我们的州并将它们发送到 "connect" 页面。

    In actions.jsx: startLoginForAuthorizedUser 操作创建者 调度一个动作以使用新的 UID & 更新 auth reducer 将用户重新路由到 "connect".

    In reducers.jsx: "auth" 状态更新为包括用户的 UID 允许组件切换元素取决于 身份验证状态。

    In App.jsx: 无论出于何种原因,mapStateToProps 未收到 更新状态,即使用户已通过身份验证并且 Redux dev 工具显示使用新 UID 更新的状态。

最终结果是经过身份验证的用户会按预期看到 "connect" 页面,但仍然会看到 logged-out header。这是代码:

App.jsx

import React from 'react';
import { connect } from 'react-redux';
import * as actions from 'actions';

import HeaderLoggedOut from './HeaderLoggedOut';
import ModalOverlay from './ModalOverlay';
import MenuWrapper from './MenuWrapper';
import Header from './Header';
import Tabs from './Tabs';

export const App = React.createClass({

    render(){

        const { uid }  = this.props;
        console.log("App.jsx: uid:", uid);

        if(!uid){
            return(
                <div>
                    <HeaderLoggedOut />
                    <div className="tonal-main">
                        <div className="tonal-content">
                            { this.props.children }
                        </div>
                    </div>
                    <ModalOverlay />
                </div>
            );
        }

        return(
            <MenuWrapper>
                <Header />
                    <div className="tonal-content">
                        { this.props.children }
                    </div>
                <Tabs />
            </MenuWrapper>
        );

    }
});

const mapStateToProps = (state) => {
    // state is not updated
    return {
        uid: state.auth.uid
    };
};

export default connect(mapStateToProps)(App);

router/index.js

import App from 'App.jsx';
import Connect from 'connect/Connect.jsx';
import firebase from 'app/firebase';

const store = require('store').configure();

firebase.auth().onAuthStateChanged((user) => {
    if(user.emailVerified && user.uid){
        store.dispatch(actions.startLoginForAuthorizedUser(user.uid));
    } else {
        browserHistory.push('/');
    }
});

const requireLogin = (nextState, replace, next) => {
    const currentUser = firebase.auth().currentUser;
    if(!currentUser){
        replace('/');
    }
    next();
};

const redirectIfLoggedIn = (nextState, replace, next) => {
    const currentUser = firebase.auth.currentUser;
    if(currentUser){
        replace('/connect');
    }
    next();
};

export default (
    <Router history={ browserHistory }>
        <Route path="/" component={ App }>
            <IndexRoute component={ Landing } onEnter={ redirectIfLoggedIn } />
            <Route path="connect" component = { Connect } onEnter = { requireLogin } />
        </Route>
    </Router>
);

Actions.jsx

// ...actions, imports, etc...

export var startLoginForAuthorizedUser = (uid) => {
    return (dispatch) => {
        dispatch(login(uid));
        dispatch(pushToRoute('/connect'));
    };
};

export var login = (uid) => {
    console.log('actions: logging in user with uid ', uid);
    return{
        type: 'LOGIN',
        uid
    };
};

export var pushToRoute = (route) => {
    browserHistory.push(route);
};

Reducer.jsx

const authInitialState = {
    uid: ""
};

export const authReducer = (state = authInitialState, action) => {
    switch(action.type){
        case 'LOGIN':
            return {
                ...state,
                uid: action.uid
            };
        case 'LOGOUT':
            return {
                ...state,
                uid: ""
            };
        default:
            return state;
    }
};

非常感谢各位专家的帮助。这让我抓狂。

经过大量令人尴尬的反复试验后,我弄明白了。对于将来想要同时使用 react-router/react-router-domfirebase 的 onAuthStateChanged() 观察者的任何其他人,只要知道你必须保留观察者代码在最顶层的组件(在 ReactDOM.render() 中呈现提供程序的地方)。

如果您将它放在其他地方,它似乎无法正常工作,坦率地说,我不知道为什么。如果更熟悉 firebase 的人想解释那里发生了什么,以及为什么它可以在顶级组件中工作,但不能在路由索引文件中工作,我相信它会对以后的人有所帮助。