2

How to use Did mount ?

My code has a problem at the time of login, when I change the screens and the auth is done. I don't know what the solution is but I think that by mistake it was the did mount that I don't know how to use: D I looked for the did mount but I don't know how to use it even more, I have to find out how it works

PROBLEM

ERROR Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
    in Home (at SceneView.tsx:122)
    (...)
    in StackNavigator (at MainStack.routes.js:11)
    in HomeStack (at App.js:32)

APP.js

import React, { useState, useEffect } from "react";
import { NavigationContainer } from "@react-navigation/native";
import MainStack from "./Routes/MainStack.routes";
import HomeStack from "./Routes/HomeStack.routes";
import auth from "@react-native-firebase/auth";
import Load from "./Components/Load";
import AsyncStorage from "@react-native-async-storage/async-storage";

export default function App() {
  const [initializing, setInitializing] = useState(true);
  const [user, setUser] = useState();

  async function onAuthStateChanged(userLoged) {
    setUser(userLoged);
    if (userLoged) {
      await AsyncStorage.setItem("lolguide@user", JSON.stringify(userLoged));
    }
    if (initializing) setInitializing(false);
  }

  useEffect(() => {
    return auth().onAuthStateChanged(onAuthStateChanged);
  }, []);

  if (initializing) {
    return <Load />;
  }

  if (!user) {
    return (
      <NavigationContainer>
        <MainStack />
      </NavigationContainer>
    );
  } else {
    return (
      <NavigationContainer>
        <HomeStack />
      </NavigationContainer>
    );
  }
}

HomeStack.js

import React from "react";
import { createStackNavigator } from "@react-navigation/stack";
import Load from "../Components/Load";
import MainStackScreen from "../Routes/MainStack.routes";
import Home from "../Pages/Home/index";
import Profile from "../Pages/Profile";
import Champ from "../Pages/Champ/index";

const MainStack = createStackNavigator();

const HomeStackScreens = () => (
  <MainStack.Navigator headerMode="none">
    <MainStack.Screen name="HomeScreen" component={Home} />
    <MainStack.Screen name="Loading" component={Load} />
    <MainStack.Screen
      name="LoginHome"
      component={MainStackScreen}
    />
    <MainStack.Screen name="Profile" component={Profile}/>
    <MainStack.Screen name="Champ" component={Champ}/>
  </MainStack.Navigator>
);

export default HomeStackScreens;

MainStack.js

import React from "react";
import { createStackNavigator } from "@react-navigation/stack";
import Login from "../Pages/Login/index";
import SignUp from "../Pages/SignUp/index";
import Load from "../Components/Load";
import Home from "../Routes/HomeStack.routes";

const MainStack = createStackNavigator();

const HomeStack = () => (
  <MainStack.Navigator headerMode="none">
    <MainStack.Screen name="Login" component={Login} />
    <MainStack.Screen name="SignUp" component={SignUp} />
    <MainStack.Screen name="Loading" component={Load} />
    <MainStack.Screen name="HomeScreen" component={Home} />
  </MainStack.Navigator>
);

export default HomeStack;

Home

import React, { useState, useEffect } from "react";
import { Container } from "./styles";
import Header from "../../Components/Header";
import Load from "../../Components/Load";
import ChampCard from "../../Components/ChampCard";
import { FlatList, StatusBar, TouchableOpacity } from "react-native";
import { useNavigation } from '@react-navigation/native';

const Home = () => {
  const [loading, setLoading] = useState(true);
  const [champs, setChamps] = useState([]);
  const navigation = useNavigation();

  function navegar(tela, props) {
    navigation.navigate(tela, {item: JSON.stringify(props)});
  }

  async function getData() {
    await fetch(
      "https://ddragon.leagueoflegends.com/cdn/11.10.1/data/pt_BR/champion.json"
    )
      .then((res) => res.json())
      .then((json) => setChamps(Object.values(json.data)));

    setLoading(false);
  }

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

  if (loading) {
    return <Load />;
  }

  return (
    <>
      <Container>
        <Header type="NormalHeader"/>
        {champs.length > 0 ? (
          <FlatList
            data={champs}
            keyExtractor={(item) => item.key}
            renderItem={(item) => (
              <TouchableOpacity onPress={() => navegar("Champ", item)}>
                <ChampCard item={item}/>
              </TouchableOpacity>
              )}
            contentContainerStyle={{ paddingBottom: 65 }}
            showsVerticalScrollIndicator={false}
          />
        ) : (
          <Load />
        )}
      </Container>
      <StatusBar backgroundColor="#000"/>
    </>
  );
};

export default Home;

Login.js

import React, { useState } from "react";
import { Image, Dimensions, PixelRatio, StyleSheet } from "react-native";
import { StatusBar } from "expo-status-bar";
import { LinearGradient } from "expo-linear-gradient";
import { useNavigation } from "@react-navigation/native";
import AsyncStorage from '@react-native-async-storage/async-storage';
import {
  Container,
  Logo,
  TextInput,
  Text,
  Button,
  ButtonArea,
  ImageBox,
  ViewAbsolute,
} from "./styles";
import Load from "../../Components/Load";
import auth from "@react-native-firebase/auth";

const widthPercentageToDP = (widthPercent) => {
  const screenWidth = Dimensions.get("window").width;
  return PixelRatio.roundToNearestPixel(
    (screenWidth * parseFloat(widthPercent)) / 100
  );
};

const heightPercentageToDP = (heightPercent) => {
  const screenHeight = Dimensions.get("window").height;
  return PixelRatio.roundToNearestPixel(
    (screenHeight * parseFloat(heightPercent)) / 100
  );
};

const styles = StyleSheet.create({
  linearGradient: {
    height: heightPercentageToDP("120%"),
    flex: 1,
  },
});

const Login = () => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [isLoading, setIsLoading] = useState(false);
  const navigation = useNavigation();

  const handleSend = () => {
    if (email && password) {
      setIsLoading(true);
      auth()
        .signInWithEmailAndPassword(email, password)
        .then( () => {
          navigation.navigate("HomeScreen");
          setIsLoading(false);
        })
        .catch((error) => {
          if (error.code) {
            console.error(error);
          }
        });
      setIsLoading(false);
    } else {
      alert("Preencha os campos!");
    }
  };

  const handleRegisterNavigate = () => {
    navigation.navigate("SignUp");
  };

  if (isLoading) {
    return <Load />;
  }

  return (
    <ViewAbsolute>
      <Container>
        <LinearGradient
          colors={["#C28F2C", "#000", "#004840"]}
          style={styles.linearGradient}
          start={{ x: 0, y: -0.2 }}
          end={{ x: 0, y: 1.2 }}
        >
          <Logo>
            <Image
              source={require("../../Assets/lolGuideIcon.png")}
              style={{ height: 75, width: 80 }}
              resizeMode="stretch"
            />
          </Logo>
          <Text>O primeiro passo para se tornar um campeão é a iniciativa</Text>
          <TextInput
            placeholder="Email"
            keyboardType="email-address"
            margin_top="50px"
            value={email}
            onChangeText={(t) => setEmail(t)}
          />
          <TextInput
            placeholder="Senha"
            margin_top="5px"
            secureTextEntry
            value={password}
            onChangeText={(t) => setPassword(t)}
          />
          <ButtonArea>
            <Button width="80px" margin_top="20px" onPress={handleSend}>
              <Text>Entrar</Text>
            </Button>
            <Button
              width="110px"
              margin_top="10px"
              onPress={handleRegisterNavigate}
            >
              <Text>Registrar</Text>
            </Button>
          </ButtonArea>
          <ImageBox>
            <Image
              source={require("../../Assets/Pyke-transparente.png")}
              style={{
                height: 230,
                width: 260,
              }}
            />
          </ImageBox>

          <StatusBar style="auto" backgroundColor="transparent" />
        </LinearGradient>
      </Container>
    </ViewAbsolute>
  );
};

export default Login;
Romero
  • 31
  • 6

3 Answers3

1

I FOUND IT!!! The problem was because I was navigating to other screen while my useEffect was working so: I remove the navigate in Login and SignUp screen and that works

import React, { useState } from "react";
import { Image, Dimensions, PixelRatio, StyleSheet } from "react-native";
import { StatusBar } from "expo-status-bar";
import { LinearGradient } from "expo-linear-gradient";
import { useNavigation } from "@react-navigation/native";
import AsyncStorage from '@react-native-async-storage/async-storage';
import {
  Container,
  Logo,
  TextInput,
  Text,
  Button,
  ButtonArea,
  ImageBox,
  ViewAbsolute,
} from "./styles";
import Load from "../../Components/Load";
import auth from "@react-native-firebase/auth";

const widthPercentageToDP = (widthPercent) => {
  const screenWidth = Dimensions.get("window").width;
  return PixelRatio.roundToNearestPixel(
    (screenWidth * parseFloat(widthPercent)) / 100
  );
};

const heightPercentageToDP = (heightPercent) => {
  const screenHeight = Dimensions.get("window").height;
  return PixelRatio.roundToNearestPixel(
    (screenHeight * parseFloat(heightPercent)) / 100
  );
};

const styles = StyleSheet.create({
  linearGradient: {
    height: heightPercentageToDP("120%"),
    flex: 1,
  },
});

const Login = () => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [isLoading, setIsLoading] = useState(false);
  const navigation = useNavigation();

  const handleSend = () => {
    if (email && password) {
      setIsLoading(true);
      auth()
        .signInWithEmailAndPassword(email, password)
        //.then( () => {
        //  navigation.navigate("HomeScreen"); REMOVE THIS BLOCK
        //  setIsLoading(false);
        //})
        .catch((error) => {
          if (error.code) {
            console.error(error);
          }
        });
      setIsLoading(false);
    } else {
      alert("Preencha os campos!");
    }
  };

  const handleRegisterNavigate = () => {
    navigation.navigate("SignUp");
  };

  if (isLoading) {
    return <Load />;
  }

  return (
    <ViewAbsolute>
      <Container>
        <LinearGradient
          colors={["#C28F2C", "#000", "#004840"]}
          style={styles.linearGradient}
          start={{ x: 0, y: -0.2 }}
          end={{ x: 0, y: 1.2 }}
        >
          <Logo>
            <Image
              source={require("../../Assets/lolGuideIcon.png")}
              style={{ height: 75, width: 80 }}
              resizeMode="stretch"
            />
          </Logo>
          <Text>O primeiro passo para se tornar um campeão é a iniciativa</Text>
          <TextInput
            placeholder="Email"
            keyboardType="email-address"
            margin_top="50px"
            value={email}
            onChangeText={(t) => setEmail(t)}
          />
          <TextInput
            placeholder="Senha"
            margin_top="5px"
            secureTextEntry
            value={password}
            onChangeText={(t) => setPassword(t)}
          />
          <ButtonArea>
            <Button width="80px" margin_top="20px" onPress={handleSend}>
              <Text>Entrar</Text>
            </Button>
            <Button
              width="110px"
              margin_top="10px"
              onPress={handleRegisterNavigate}
            >
              <Text>Registrar</Text>
            </Button>
          </ButtonArea>
          <ImageBox>
            <Image
              source={require("../../Assets/Pyke-transparente.png")}
              style={{
                height: 230,
                width: 260,
              }}
            />
          </ImageBox>

          <StatusBar style="auto" backgroundColor="transparent" />
        </LinearGradient>
      </Container>
    </ViewAbsolute>
  );
};

export default Login;
Romero
  • 31
  • 6
0

The onAuthStateChanged function makes the dependencies of useEffect() Hook change on every render. Move it inside the useEffect callback. Alternatively, you can wrap the definition of onAuthStateChanged in its own useCallback() Hook.

The updated code for App.js:-

import React, { useState, useEffect } from "react";
import { NavigationContainer } from "@react-navigation/native";
import MainStack from "./Routes/MainStack.routes";
import HomeStack from "./Routes/HomeStack.routes";
import auth from "@react-native-firebase/auth";
import Load from "./Components/Load";
import AsyncStorage from "@react-native-async-storage/async-storage";

export default function App() {
  
  const [initializing, setInitializing] = useState(true);
  const [user, setUser] = useState();

  useEffect(() => {
    auth().onAuthStateChanged((userLogged) => {
      setUser(userLogged);
      if (userLogged) {
        AsyncStorage.setItem("lolguide@user", JSON.stringify(userLogged));
      }
      if (initializing) setInitializing(false);
    });
  }, [initializing]);

  if (initializing) {
    return <Load />;
  }

  if (!user) {
    return (
      <NavigationContainer>
        <MainStack />
      </NavigationContainer>
    );
  } else {
    return (
      <NavigationContainer>
        <HomeStack />
      </NavigationContainer>
    );
  }
}
Rohan
  • 52
  • 1
  • 4
0

Memory leak usually when be will happened , you in the asynchronous function after that your component was unmounted , update your states. you are able to handle it by useRef (as one of the solutions) , So try followings this , i hope this help you:

Home

import React, { useState, useEffect } from "react";
import { Container } from "./styles";
import Header from "../../Components/Header";
import Load from "../../Components/Load";
import ChampCard from "../../Components/ChampCard";
import { FlatList, StatusBar, TouchableOpacity } from "react-native";
import { useNavigation } from '@react-navigation/native';

const Home = () => {
  const isMounted = React.useRef(null);

  const [loading, setLoading] = useState(true);
  const [champs, setChamps] = useState([]);
  const navigation = useNavigation();

  function navegar(tela, props) {
    navigation.navigate(tela, {item: JSON.stringify(props)});
  }

  async function getData() {
    await fetch(
      "https://ddragon.leagueoflegends.com/cdn/11.10.1/data/pt_BR/champion.json"
    )
      .then((res) => res.json())
      .then((json) => {
          if (isMounted.current)
             setChamps(Object.values(json.data)));
      }
    setLoading(false);
  }

  useEffect(() => {
    isMounted.current = true;

    getData();

    return ()=> {
        isMounted.current = false;
    };
  }, []);

  if (loading) {
    return <Load />;
  }

  return (
    <>
      <Container>
        <Header type="NormalHeader"/>
        {champs.length > 0 ? (
          <FlatList
            data={champs}
            keyExtractor={(item) => item.key}
            renderItem={(item) => (
              <TouchableOpacity onPress={() => navegar("Champ", item)}>
                <ChampCard item={item}/>
              </TouchableOpacity>
              )}
            contentContainerStyle={{ paddingBottom: 65 }}
            showsVerticalScrollIndicator={false}
          />
        ) : (
          <Load />
        )}
      </Container>
      <StatusBar backgroundColor="#000"/>
    </>
  );
};

export default Home;

Also if you see this warning whenever , you can use the approach for handle that.

MHP
  • 577
  • 5
  • 9
  • I have stop to seeing this error for the first time, but when I signout and log in again the error comes again, how can I handle that – Romero May 25 '21 at 15:30
  • Did you received the same warning or the warning has difference ? also did you checking out the Login screen? – MHP May 25 '21 at 17:17
  • The same warning message Could you check my login screen ? – Romero May 25 '21 at 18:09