Redux / Firebase — компонент не обновляется после получения uid в состоянии

#reactjs #redux #react-router #firebase-authentication #redux-thunk

#reactjs #redux #реагировать-маршрутизатор #firebase-аутентификация #redux-thunk

Вопрос:

Я использую API аутентификации Firebase для своего приложения Redux. У меня есть компонент приложения, который должен получать информацию при получении аутентификации пользователя по UID и переключать, какой заголовок отображается пользователю (либо заголовок входа в систему, либо заголовок выхода из системы). Однако в случаях, когда пользователь уже вошел в систему и повторно посещает основной маршрут, я не могу заставить мое основное приложение повторно отображать, когда UID сохраняется в состоянии приложения.

Схема потока такова:

  1. В routes/index.js : Наблюдатель onAuthStateChanged Firebase определяет, вошел ли пользователь уже в систему или нет, когда загружается основной маршрут. Если есть пользователь, отправьте действия для копирования его UID из Firebase в наше состояние и отправьте их на страницу «подключиться».

    В actions.jsx: создатель действия startLoginForAuthorizedUser отправляет действие для обновления редуктора аутентификации с помощью нового UID и перенаправляет пользователя на «connect».

    В reducers.jsx: состояние «auth» обновляется, чтобы включить UID пользователя, чтобы позволить компонентам переключать элементы в зависимости от статуса аутентификации.

    В App.jsx: по какой-то причине mapStateToProps не получает обновленное состояние, даже если пользователь аутентифицирован, а Redux dev tools показывает состояние как обновленное с новым UID.

Конечным результатом является то, что аутентифицированные пользователи видят страницу «подключиться», как и ожидалось, но все равно видят заголовок выхода из системы. Вот код:

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 amp;amp; 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;
    }
};
  

Любая помощь от ваших экспертов была бы весьма признательна. Это сводит меня с ума.

Ответ №1:

Я понял это после смущающего количества проб и ошибок. Для всех, кто в будущем захочет использовать как react-router / react-router-dom , так и firebase onAuthStateChanged() observer, просто знайте, что вы должны сохранить код наблюдателя в своем самом верхнем компоненте (где вы отображаете своего провайдера ReactDOM.render() ).

Если вы сохраните его где-нибудь еще, он, похоже, не работает должным образом, и я, честно говоря, понятия не имею, почему. Если кто-то, более знакомый с firebase, захочет объяснить, что там происходит и почему это будет работать в компоненте верхнего уровня, но не в индексном файле маршрутов, я уверен, что это поможет кому-то в будущем.

Комментарии:

1. Я не думаю, что это смущение, а шаг вперед! Я потратил 4 дня на то, чтобы разобраться, и это было связано с ` и»!