React Native-已卸载组件上的React状态更新

时间:2020-01-02 16:05:59

标签: react-native react-navigation setstate

screen shot of warning

我是React Native的新手,在浏览我的应用程序组件时,我随机地在不同的屏幕中遇到此警告

ExceptionsManager.js:82警告:无法执行React state update on an unmounted component。这是空操作,但它表明应用程序中发生内存泄漏。要解决此问题,请在componentWillUnmount方法中取消所有订阅和异步任务。 在LoginScreen中(由n创建) 我尝试使用stackoverflow中找到的其他解决方案,但是会发生这种情况,并且在警告此应用程序之后,如果对我的应用程序执行任何额外操作,我将对应用程序使用setState冻结。

我尝试: -将此this._isMounted在componentDidMount中设置为true,在componentWillMount中设置为false。 -使用fetch更改呼叫我的请求并使用axios。 -更改导航以将导航从一个屏幕推送或替换为另一个屏幕。

我没事。

这是我的登录屏幕代码:

import React, { Component } from "react";
import {
  StyleSheet,
  View,
  Image,
  ImageBackground,
  TextInput,
  Text,
  Alert,
  TouchableOpacity
} from "react-native";
import IMAGELOGINBG from "../../Images/login_bg.png"; //'../../index.js';
import { Button } from "react-native-elements";
import { LinearGradient } from "expo-linear-gradient";
import USERICON from "../../Images/user-icon.png";
import PASSWORDICON from "../../Images/pass-icon.png";
import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
// import {login} from './service.js';
import { BackHandler } from "react-native";
import ProgressLoader from "rn-progress-loader";
import withUnmounted from "@ishawnwang/withunmounted";

import yelp from "../api/yelp.js";

export function updateState(msg) {
  if (isMounted == true) {
    this.props.navigation.setParams({
      notificationCount: global.NotificationCount
    });
    this.setState(
      {
        visible: false,
        errorMsg: msg
      },
      () => {
        this.timer = setTimeout(() => {
          Alert.alert(msg);
          if (global.loggedInControllers) {
            this.props.navigation.navigate("ControllersScreen");
          }
        });
      }
    );
  }
}
const login = async (userName, password) => {
  var url =
    "Login" +
    "/" +
    global.DeviceId +
    "/" +
    userName +
    "/" +
    password +
    "/" +
    global.OS;
  try {
    const response = await yelp.post(`/${url}`);
    console.log(response.data);
    var data = response.data;
    global.loggedInControllers = data.Data;
    if (data.NotificationsCount) {
      global.NotificationCount = data.NotificationsCount;
    }
    _storeLoggedInUser(userName);
    _getLoggedInUser();

    updateState(data.ResponseMessage);
  } catch (e) {
    console.log(e);
    updateState(e);
  }
};

_storeLoggedInUser = async user => {
  try {
    await AsyncStorage.setItem("UserName", user);
  } catch (error) {
    console.log("Something went wrong", error);
  }
};
_getLoggedInUser = async () => {
  try {
    let name = await AsyncStorage.getItem("UserName");
    console.log("AFTER LOGIN " + name);
  } catch (error) {
    console.log("Something went wrong", error);
  }
};
class LoginScreen extends Component {
  constructor(props) {
    super(props);
    this.handleBackButtonClick = this.handleBackButtonClick.bind(this);
    this.state = {
      visible: false,
      userName: "",
      password: "",
      errorMsg: ""
    };
    isMounted = false;
    updateState = updateState.bind(this);
  }

  componentDidMount() {
    const { navigation } = this.props;

    //Adding an event listner om focus
    //So whenever the screen will have focus it will set the state to zero
    this.focusListener = navigation.addListener("didFocus", () => {
      isMounted = true;
    });
  }

  componentWillMount() {
    BackHandler.addEventListener(
      "hardwareBackPress",
      this.handleBackButtonClick
    );
  }

  componentWillUnmount() {
    clearInterval(this.timer);
    this.focusListener.remove();
    BackHandler.removeEventListener(
      "hardwareBackPress",
      this.handleBackButtonClick
    );
    isMounted = false;
  }

  handleBackButtonClick() {
    this.props.navigation.goBack(null);
    return true;
  }

  state = {};

  validate(text) {
    var regx = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
    if (!regx.test(text)) {
      return false;
    } else {
      return true;
    }
  }

  onClickBackToMainScreen() {
    this.props.navigation.navigate("MainScreen");
  }
  onClickLogin() {
    const { userName, password } = this.state;

    if (userName.trim() === "") {
      this.state.errorMsg = "Please enter username";
      alert(this.state.errorMsg);
    }
    // else if (!this.validate(userName)) {
    //   this.state.errorMsg = 'Please enter valid email';
    //   alert(this.state.errorMsg)
    // }
    else if (password.trim() === "") {
      this.state.errorMsg = "Please enter password";

      alert(this.state.errorMsg);
    } else {
      this.setState({
        visible: !this.state.visible
      });
      login(this.state.userName, this.state.password);
    }
  }

  render() {
    return (
      <ImageBackground source={IMAGELOGINBG} style={styles.bgContainer}>
        <KeyboardAwareScrollView
          style={styles.scrollContainer}
          contentContainerStyle={{ flexGrow: 1 }}
          innerRef={ref => {
            this.scroll = ref;
          }}
          onKeyboardWillShow={frames => {
            console.log("Keyboard event", frames);
          }}
        >
          <View style={styles.bigContainer}>
            <View style={styles.container}>
              <View style={styles.inputContainer}>
                <Image source={USERICON} style={styles.inputIconImage} />
                <TextInput
                  onFocus={event => {}}
                  allowFontScaling={false}
                  style={styles.editText}
                  placeholder="Username"
                  placeholderTextColor="gray"
                  underlineColorAndroid="transparent"
                  value={this.state.userName}
                  keyboardType="email-address"
                  onChangeText={userName => this.setState({ userName })}
                />
              </View>

              <View style={styles.inputContainer}>
                <Image source={PASSWORDICON} style={styles.inputIconImage} />
                <TextInput
                  allowFontScaling={false}
                  style={styles.editText}
                  placeholder="Password"
                  placeholderTextColor="gray"
                  underlineColorAndroid="transparent"
                  value={this.state.password}
                  onChangeText={password => this.setState({ password })}
                  secureTextEntry={true}
                />
              </View>

              <LinearGradient
                colors={["#870000", "#540000", "#2f0000"]}
                style={styles.buttonContainer}
                start={{ y: 0.0, x: 0.0 }}
                end={{ y: 0.0, x: 1.0 }}
              >
                <TouchableOpacity
                  style={styles.button}
                  onPress={this.onClickLogin.bind(this)}
                >
                  <Text allowFontScaling={false} style={styles.textButton}>
                    Login
                  </Text>
                </TouchableOpacity>
              </LinearGradient>

              <Text
                allowFontScaling={false}
                style={styles.textUnderline}
                onPress={this.onClickBackToMainScreen.bind(this)}
              >
                Back to scan controller
              </Text>
            </View>
          </View>
          {/* </ScrollView> */}
        </KeyboardAwareScrollView>
        <View style={{ height: 80, width: 80, justifyContent: "center" }}>
          <ProgressLoader
            visible={this.state.visible}
            isModal={true}
            isHUD={true}
            hudColor={"#fff"}
            color={"#000"}
          />
        </View>
      </ImageBackground>
    );
  }
}
var styles = StyleSheet.create({
  bgContainer: {
    width: "100%",
    height: "100%",
    justifyContent: "center",
    alignItems: "center"
  },

  bigContainer: {
    width: "100%",
    height: "100%",
    flex: 1,
    justifyContent: "center",
    alignItems: "center"
  },
  scrollContainer: {
    width: "100%",
    height: "100%",
    flex: 1
  },
  container: {
    height: 222,
    width: "80%",
    flexDirection: "column",
    alignItems: "center",
    position: "absolute", //Here is the trick
    bottom: 50 //Here is the trick
  },
  button: {
    height: "100%",
    width: "100%",

    // fontSize: 22,
    backgroundColor: "transparent"
  },
  buttonContainer: {
    height: 62,
    width: "100%",
    borderRadius: 30
  },
  inputContainer: {
    height: 60,
    width: "100%",
    borderRadius: 50,
    borderWidth: 1,
    borderColor: "#fff",
    marginBottom: 20,
    flexDirection: "row",
    alignItems: "center",
    backgroundColor: "#000",
    opacity: 0.7
  },
  editText: {
    height: "100%",
    color: "#fff",
    textAlignVertical: "top",
    fontSize: 20,
    width: "70%",
    backgroundColor: "#000",
    opacity: 0.7
  },
  bgImage: {
    flex: 1,
    width: undefined,
    height: undefined
  },
  inputIconImage: {
    width: "8%",
    height: 25,
    marginRight: "5%",
    resizeMode: "contain",
    marginLeft: "7%"
  },
  textUnderline: {
    textDecorationLine: "underline",
    textDecorationStyle: "solid",
    color: "#fff",
    marginTop: 10,
    paddingBottom: 3,
    fontSize: 18
  },
  textButton: {
    color: "#fff",
    fontSize: 16,
    textAlign: "center",
    textAlignVertical: "center",
    width: "100%",
    height: "100%",
    lineHeight: 62,
    alignSelf: "center"
  }
});
export default withUnmounted(LoginScreen);

1 个答案:

答案 0 :(得分:0)

这可能与barisMounted有关。从组件内部以及在updateState函数本身内部引用这两者时,您需要使用updateStatethis.isMounted

但是,this.updateState对于组件来说有点奇怪。如果未安装组件,则此属性将不再存在,也将不再使用该方法来检查它。如果在未安装组件时仍在调用isMounted,那将是一个更深层次的问题。

无论如何,请告诉我,在对updateStatethis.的每个引用前面都添加isMounted