#reactjs #redux #react-router #firebase-authentication #redux-thunk
#reactjs #redux #реагировать-маршрутизатор #firebase-аутентификация #redux-thunk
Вопрос:
Я использую API аутентификации Firebase для своего приложения Redux. У меня есть компонент приложения, который должен получать информацию при получении аутентификации пользователя по UID и переключать, какой заголовок отображается пользователю (либо заголовок входа в систему, либо заголовок выхода из системы). Однако в случаях, когда пользователь уже вошел в систему и повторно посещает основной маршрут, я не могу заставить мое основное приложение повторно отображать, когда UID сохраняется в состоянии приложения.
Схема потока такова:
-
В 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 дня на то, чтобы разобраться, и это было связано с ` и»!