Могу ли я исправить ошибку `` Не удается обновить во время существующего перехода в состояние`) при реакции при использовании аутентификации в firebase? - PullRequest
0 голосов
/ 20 апреля 2019

Я пытался создать логин с помощью 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, и он работал.Затем я решил провести рефакторинг, потому что хотел сохранить все, что касается моего провайдера входа в систему, в другом классе, чтобы я мог переключиться на поддельную реализацию для целей тестирования.

Поэтому я реализовал это следующим образом:

LoginProvider.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
    }

}

Login.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.Не могли бы вы помочь мне решить проблему?

1 Ответ

0 голосов
/ 20 апреля 2019

Хорошо, я решил это. Я не знал о 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...