使用实时 Firebase 数据库的 React 应用程序内存泄漏

IT技术 reactjs firebase-realtime-database memory-leaks axios
2021-05-03 05:52:54

我不知道如何使用 useEffect(),我不断收到

无法对卸载的组件执行 React 状态更新。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要修复,请取消 useEffect 清理函数中的所有订阅和异步任务。 在登录

这是我的组件:

import React, { Component, useState, useEffect } from 'react';
import fire from '../fire';

import Sign from './sign';
import Dashboard from './dashboard';
import Map from './journey/map';

const Login = () => {


const [user, setUser ] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [emailError, setEmailError] = useState('');
const [passwordError, setPasswordError] = useState('');
const [hasAccount, setHasAccount] = useState(false);


const clearInputs = () => {
    setEmail('');
    setPassword('');
}

const clearErrors = () => {
    setEmailError('');
    setPasswordError('');
}

const handleLogin = () => {
    clearErrors();
    fire
        .auth()
        .signInWithEmailAndPassword(email, password)
        .catch(err => {
            switch(err.code){
                case 'auth/invalid-email':
                case 'auth/user-disabled':
                case 'auth/user-not-found':
                    setEmailError(err.message);
                    break;
                case 'auth/wrong-password':
                    setPasswordError(err.message);
                    break;
            }
        });
}

const handleSignUp = () => {
    clearErrors();
    fire
    .auth()
    .createUserWithEmailAndPassword(email, password)
    .catch(err => {
        switch(err.code){
            case 'auth/email-already-in-use':
            case 'auth/invalid-email':
                setEmailError(err.message);
                break;
            case 'auth/weak-password':
                setPasswordError(err.message);
                break;
        }
    });
    return 
}

const handleLogOut = () => {
    fire.auth().signOut();
};


const authListener = (user_id) => {
    user_id = '';
    fire.auth().onAuthStateChanged(user => {
        if(user) {
            clearInputs();
            setUser(user);
            user_id = user.email;
        } else {
            setUser('');
        }
    });
}

useEffect(() => {
    authListener();
    
}, []);

    return (
        
        <div>
            
            {user ? (
                <div>
                <Dashboard 
                handleLogOut={handleLogOut}
                user_id={user.email}
                />
                <Map 
                user_id={user.email}
                />
                </div>
            ) : (
                <Sign 
                email={email}
                setEmail={setEmail}
                password={password}
                setPassword={setPassword}
                handleLogin={handleLogin}
                handleSignUp={handleSignUp}
                hasAccount={hasAccount}
                setHasAccount={setHasAccount}
                emailError={emailError}
                passwordError={passwordError}
                />  
                
            )}
            
            
        

        </div>
    );

}

export default Login;

我正在使用 Firebase 身份验证。

注意只有当我进入子组件然后返回并注销应用程序时才会发生内存泄漏。这是子组件。

import React, { Component } from 'react';
import axios from 'axios';
import { Link } from "react-router-dom";

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faCaretSquareLeft } from '@fortawesome/free-solid-svg-icons'

import MapItem from '../mapItems';
import Card from './card';

class Cards extends Component {
constructor(props) {
    super(props);

    this.state = {
        journey_name: [],
        cards: [],
        data: [],
        hero: [],
        loading: true
    }

    this.cards = this.cards.bind(this);        
}


fetchCards = () => {

    axios.get('myapi').then(response => {
        console.log('fetchCards',response);
        this.setState({
            journey_name: response.data.journey_name,
            cards: response.data.cards,
        })
    }).catch(error => {
        console.log(`fetchCards error : ${error}`);
    }) 

}

fetchData = () => {
    axios.get('my api').then(response => {
        console.log('fetchdata',response);
        this.setState({
            data: response.data,
            hero: response.data.hero
        })
    }).catch(error => {
        console.log(`fetchData error : ${error}`);
    })
}

componentDidMount() {
    this.fetchCards();
    this.fetchData();

}

cards = () => {
    return (
        this.state.cards.map(card => {
            return (
                <Card
                    key={card.card_id}
                    card_name={card.card_name}
                    explanation={card.explanation}
                    image_url={card.image}
                    points={card.points}
                    reference={card.reference}
                />
            )
        })
    )
}


render() {

    return (
        <div className='cards-page'>
            <div className='cards-page__navbar'>
                <Link to='/'>
                    <FontAwesomeIcon icon={faCaretSquareLeft} />
                </Link>
                <MapItem
                    name={this.state.data.name}
                    level={this.state.hero.level}
                    points={this.state.hero.points}
                    class='cards-user'
                />
            </div>

            <div className='cards-rail'>
                {this.cards()}
            </div>

            <div className='footer'> by Johnattan M Angeles </div>
        </div>
    );
}
}

export default Cards;
1个回答

虽然您可能应该取消订阅 firebase onAuthStateChanged 事件,但我认为您的问题可能与 axios 获取请求有关。如果它们稍后您导航离开子组件后解决则它们正在更新未安装组件的状态。当组件卸载时,您应该取消任何挂起的请求。

class Cards extends Component {
  constructor(props) {
    super(props);

    ...

    // Create cancel token & source
    const CancelToken = axio.CancelToken;
    this.source = CancelToken.source();
  }


  fetchCards = () => {
    axios.get(
      'myapi',
      { cancelToken: this.source.token }, // <-- pass cancel token W/ request
    ).then(response => {
      console.log('fetchCards',response);
      this.setState({
        journey_name: response.data.journey_name,
        cards: response.data.cards,
      })
    }).catch(error => {
      console.log(`fetchCards error : ${error}`);
    }) 

  }

  fetchData = () => {
    axios.get(
      'my api',
      { cancelToken: source.token }, // <-- pass cancel token W/ request
    ).then(response => {
      console.log('fetchdata',response);
      this.setState({
        data: response.data,
        hero: response.data.hero
      })
    }).catch(error => {
      console.log(`fetchData error : ${error}`);
    })
  }

  componentDidMount() {
    this.fetchCards();
    this.fetchData();
  }

  componentWillUnmount() {
    this.source.cancel('Operation cancelled by user'); // <-- cancel on unmount
  }

  ...
}