reactjs - 我可以在使用 Firebase 身份验证时修复“无法在现有状态转换期间更新”错误吗?
问题描述
我正在尝试使用该项目使用 firebase 实现登录firebaseui-web-react
。
我正在关注人们所做的这个例子firebaseui-web-react
。您可以在此处找到基本文件,但我会将其粘贴到这个问题上:
/**
* Copyright 2017 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// React core.
import React from 'react';
import ReactDOM from 'react-dom';
// Firebase.
import firebase from 'firebase/app';
import 'firebase/auth';
import StyledFirebaseAuth from 'react-firebaseui/StyledFirebaseAuth';
// Styles
import styles from './app.css'; // This uses CSS modules.
import './firebaseui-styling.global.css'; // Import globally.
// Get the Firebase config from the auto generated file.
const firebaseConfig = require('./firebase-config.json').result;
// Instantiate a Firebase app.
const firebaseApp = firebase.initializeApp(firebaseConfig);
/**
* The Splash Page containing the login UI.
*/
class App extends React.Component {
uiConfig = {
signInFlow: 'popup',
signInOptions: [
firebase.auth.GoogleAuthProvider.PROVIDER_ID,
firebase.auth.EmailAuthProvider.PROVIDER_ID,
],
callbacks: {
signInSuccessWithAuthResult: () => false,
},
};
state = {
isSignedIn: undefined,
};
/**
* @inheritDoc
*/
componentDidMount() {
this.unregisterAuthObserver = firebaseApp.auth().onAuthStateChanged((user) => {
this.setState({isSignedIn: !!user});
});
}
/**
* @inheritDoc
*/
componentWillUnmount() {
this.unregisterAuthObserver();
}
/**
* @inheritDoc
*/
render() {
return (
<div className={styles.container}>
<div className={styles.logo}>
<i className={styles.logoIcon + ' material-icons'}>photo</i> My App
</div>
<div className={styles.caption}>This is a cool demo app</div>
{this.state.isSignedIn !== undefined && !this.state.isSignedIn &&
<div>
<StyledFirebaseAuth className={styles.firebaseUi} uiConfig={this.uiConfig}
firebaseAuth={firebaseApp.auth()}/>
</div>
}
{this.state.isSignedIn &&
<div className={styles.signedIn}>
Hello {firebaseApp.auth().currentUser.displayName}. You are now signed In!
<a className={styles.button} onClick={() => firebaseApp.auth().signOut()}>Sign-out</a>
</div>
}
</div>
);
}
}
// Load the app in the browser.
ReactDOM.render(<App/>, document.getElementById('app'));
我使用 Typescript 实现了它并且它正在工作。然后我决定重构它,因为我想将有关我的登录提供程序的所有内容保存在不同的类中,以便我可以切换到假实现以进行测试。
所以我以这种方式实现了它:
登录提供者.ts
export default interface LoginProvider {
unregister(): void
register(f:(user:any)=>void): void
actualProvider(): any
actualConfig(): any
}
FirebaseLoginProvider.ts
import LoginProvider from './LoginProvider';
export default class FirebaseLoginProvider implements LoginProvider {
private firebaseApp: any
private uiConfig: any
private unregisterAuthObserver: () => void
constructor(firebaseApp: any, uiConfig: any) {
this.uiConfig = uiConfig
this.firebaseApp = firebaseApp
}
public register(f:(user:any)=>void): void {
this.unregisterAuthObserver = this.firebaseApp.auth().onAuthStateChanged((user: any) => {
console.log("state changed " + !!user)
f(user)
}).bind(this)
}
public unregister(): void {
this.unregisterAuthObserver()
}
public actualProvider(): any {
return this.firebaseApp.auth()
}
public actualConfig(): any {
return this.uiConfig
}
}
登录.tsx
import 'firebase/auth';
import './firebase-global.css';
import * as React from 'react';
import { RouteComponentProps, withRouter } from "react-router-dom";
import LoginProvider from './LoginProvider';
import StyledFirebaseAuth from 'react-firebaseui/StyledFirebaseAuth';
interface Props {
};
interface State {
isSignedIn: boolean
}
interface HomeProps extends RouteComponentProps<Props> {
loginProvider: LoginProvider
}
class Login extends React.Component<HomeProps, State> {
state = {
isSignedIn: false
}
constructor(props: HomeProps) {
super(props);
}
componentDidMount() {
this.props.loginProvider.register((user) => {
this.setState({ isSignedIn: !!user } as State);
})
}
componentWillUnmount() {
this.props.loginProvider.unregister();
}
render() {
const signedIn = this.state.isSignedIn
return (
<div className='container'>
{!signedIn &&
<div>
<StyledFirebaseAuth
className='firebaseUi'
uiConfig={this.props.loginProvider.actualConfig()}
firebaseAuth={this.props.loginProvider.actualProvider()} />
</div>
}
{signedIn && this.props.history.goBack()}
</div>
);
}
}
export default withRouter(Login);
我的实现工作正常,但我在浏览器控制台中收到此警告,我不明白为什么。
Warning: Cannot update during an existing state transition (such as within 'render'). Render methods should be a pure function of props and state.
我也有以下堆栈跟踪:
React 8
listener createTransitionManager.js:46
notifyListeners createTransitionManager.js:65
notifyListeners createTransitionManager.js:64
setState createBrowserHistory.js:78
handlePop createBrowserHistory.js:103
confirmTransitionTo createTransitionManager.js:36
handlePop createBrowserHistory.js:101
handlePopState createBrowserHistory.js:85
go createBrowserHistory.js:214
goBack createBrowserHistory.js:218
render Login.tsx:55
React 13
componentDidMount Login.tsx:34
unregisterAuthObserver FirebaseLoginProvider.ts:16
next index.cjs.js:1303
sendOne index.cjs.js:1407
关键是我没有看到其他实现的问题,但我只是将东西提取到方法和类中,所以我不明白为什么它给了我这个以前没有的错误。
问题似乎与react-router
我尝试做的地方有关:
{signedIn && this.props.history.goBack()}
但是如果我把这个调用放在里面componentDidMount
,在firebase登录页面关闭后它就不会被调用。
我可能遗漏了一些关于 React 生命周期的东西。你能帮我解决这个问题吗?
解决方案
好的,我解决了。我不知道componentDidUpdate
。
这是没有警告的新实现:
import 'firebase/auth';
import './firebase-global.css';
import * as React from 'react';
import { RouteComponentProps, withRouter } from "react-router-dom";
import LoginProvider from './LoginProvider';
import StyledFirebaseAuth from 'react-firebaseui/StyledFirebaseAuth';
interface Props {
};
interface State {
isSignedIn: boolean
}
interface HomeProps extends RouteComponentProps<Props> {
loginProvider: LoginProvider
}
class Login extends React.Component<HomeProps, State> {
state = {
isSignedIn: false
}
constructor(props: HomeProps) {
super(props);
}
componentDidMount() {
this.props.loginProvider.register((user) => {
this.setState({ isSignedIn: !!user } as State);
})
}
componentDidUpdate() {
this.goBackWhenLoggedIn();
}
componentWillUnmount() {
this.props.loginProvider.unregister();
}
render() {
const signedIn = this.state.isSignedIn
return (
<div className='container'>
{!signedIn &&
<div>
<StyledFirebaseAuth
className='firebaseUi'
uiConfig={this.props.loginProvider.actualConfig()}
firebaseAuth={this.props.loginProvider.actualProvider()} />
</div>
}
</div>
);
}
private goBackWhenLoggedIn() {
if (this.state.isSignedIn) {
this.props.history.goBack()
}
}
}
export default withRouter(Login);
推荐阅读
- sql - 如何从 SQL Server 上的 XML 列中选择数据?
- json - JSON字符串序列与我的JSON类不同,请建议我如何更正序列
- scala - 命中数超过总请求数
- perl - 有没有更好的方法来引用哈希?
- unicode - Encoding binary into unicode
- express - Google Functions(免费)尝试访问 reCaptcha 验证时出错
- flutter - Flutter中提供者有什么用?
- python - How to fix a pyodbc connection, make a query and read as pandas Dataframe
- java - SharedPreferences key setted before creation
- arrays - 打印一个数组真的是 O(n) 而不是 O(1)?