简体   繁体   English

警告:无法对未安装的组件执行 React state 更新。 如何使用do mount

[英]Warning: Can't perform a React state update on an unmounted component. How to use did mount

How to use Did mount?如何使用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: DI looked for the did mount but I don't know how to use it even more, I have to find out how it works我不知道解决方案是什么,但我认为错误地是我不知道如何使用的安装:DI 寻找了安装,但我不知道如何使用它,我必须找出它是如何工作的

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 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 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 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登录.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;

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问题是因为我在 useEffect 工作时导航到其他屏幕,所以:我删除了登录和注册屏幕中的导航,并且有效

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;

The onAuthStateChanged function makes the dependencies of useEffect() Hook change on every render. onAuthStateChanged function 使useEffect() Hook 的依赖关系在每次渲染时都发生变化。 Move it inside the useEffect callback.将它useEffect回调中。 Alternatively, you can wrap the definition of onAuthStateChanged in its own useCallback() Hook.或者,您可以将onAuthStateChanged的定义包装在它自己的useCallback() Hook 中。

The updated code for App.js :- 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>
    );
  }
}

Memory leak usually when be will happened, you in the asynchronous function after that your component was unmounted, update your states. Memory 泄漏通常什么时候会发生,你在异步 function 之后你的组件被卸载,更新你的状态。 you are able to handle it by useRef (as one of the solutions), So try followings this, i hope this help you:您可以通过useRef处理它(作为解决方案之一),因此请尝试以下操作,希望对您有所帮助:

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.此外,如果您随时看到此警告,则可以使用该方法进行处理。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 React 警告:无法对卸载的组件执行 React 状态更新。 要修复,请取消所有订阅 - React Warning: Can't perform a React state update on an unmounted component. To fix, cancel all subscriptions React:ComponentDidMount 中的 SetInterval 导致错误“警告:无法在未安装的组件上执行 React 状态更新。” - React: SetInterval in ComponentDidMount causing error "Warning: Can't perform a React state update on an unmounted component." 警告:无法对未安装的组件执行 React state 更新。 Firebase 本机反应 - Warning: Can't perform a React state update on an unmounted component. Firebase React Native 警告:无法对未安装的组件执行 React state 更新。 使用效果清理 function - Warning: Can't perform a React state update on an unmounted component. useEffect cleanup function 如何修复“无法在未安装的组件上执行React状态更新。” React Native中的错误 - How to fix “Can't perform a React state update on an unmounted component.” error in React Native 警告:无法对未安装的组件执行 React state 更新。 这是一个空操作,但它表明您的应用程序中存在 memory 泄漏 - 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 无法对卸载的组件警告执行React状态更新 - Can't perform a React state update on an unmounted component warning 警告:无法对未安装的组件执行 React state 更新 - Warning: Can't perform a React state update on an unmounted component 无法对卸载的组件执行 React 状态更新。 (类组件) - Can't perform a React state update on an unmounted component. (Class component) 无法对未安装的组件执行 React state 更新。 使用效果 React-Native - Can't perform a React state update on an unmounted component. useEffect React-Native
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM