Joko Joko
Joko Joko

Reputation: 121

How to re-render after state change in React-Native?

I am developing React Native app, where must be overlay view before starting. Like "welcome screen". After you have read content in overlay view, you press ok.

setState is not working. Throw error TypeError: _this.setState is not a function

I'll tried to use setState like this:

removeElement = () => {
  //this.state.loadingScreen = false;
  this.setState({
    loadingScreen: false
  })
  console.log(`Staten arvo nyt: ` + this.state.loadingScreen);

When I use setState it should re-render component, but I don't know what is the reason why it not works. I can change state this.state.loadingScreen = false;, but it is not re-rendering. And forceUpdate() not working either.

QUESTION: How can I render component again to get rid off overlay view?

My code:

import React, { useState, setState, Component } from "react";
import { Text, View, StyleSheet, TextInput, Button, Alert, TouchableOpacity} from "react-native";
import AsyncStorage from '@react-native-community/async-storage';

export default function StartPage({ navigation }) {

  state = {  
    loadingScreen: true,
  }

  let userInput = "";
  let userInputName = "";

  readUserInput = (text) => {
    userInput = text
  }
  readUserInputName = (text) => {
    userInputName = text
  }
  checkUserInput = () => {
  if(userInput.length < 1 && userInputName.length < 1){
      Alert.alert("Tarkista rekisterinumero ja nimi")
    }
    else{
      storeData = async () => {
        try{
            AsyncStorage.setItem("RegistrationNumber", userInput);
            AsyncStorage.setItem("UserName", userInputName);           
        }
        catch(e){
            console.log(e);
        }
      }
      storeData();
      navigation.navigate("GeneralInspection")
    }
  }
  renderElement = () =>{
    if(this.state.loadingScreen == true)
       return <View style={styles.fullScreen}>
       <TouchableOpacity style={styles.button} onPress={this.removeElement}>
          <Text style={{fontSize:20}}>Change state to false</Text>
        </TouchableOpacity>
    </View>;;
       
    return null;
 }

 removeElement = () => {
  this.state.loadingScreen = false
 }

 setTimeout(
  function() {
      
  }
  .bind(this),
  1000
);

  return (
    <View style={styles.view}>
        { this.renderElement() }
    <Text style={styles.text}>Tiedot</Text>
    <TextInput
      style={styles.inputStyle}
      placeholder="Nimi"
      onChangeText={this.readUserInputName}
      autoCapitalize="words"
    />
    <TextInput
      style={styles.inputStyle}
      placeholder="ABC-123"
      onChangeText={this.readUserInput}
      autoCapitalize="characters"
    />
    <TouchableOpacity style={styles.button} onPress={this.checkUserInput}>
      <Text style={{fontSize:20}}>Aloita</Text>
    </TouchableOpacity>
  </View>
  );
}



const styles = StyleSheet.create({
  view: {
    height: "100%",
    width: "100%",
    justifyContent: "center",
    alignItems: "center",
  },
  text: {
    fontSize: 40,
    fontWeight: "bold",
    padding:10,
    fontStyle:"italic"
  },
  inputStyle: {
    height:50,
    borderColor:"black",
    borderWidth:1,
    width:"50%",
    marginBottom:15,
    textAlign:"center",
    fontSize: 20,
    borderRadius:5,
  },
  button: {
    backgroundColor:"#007bff",
    borderRadius:5,
    padding:8,
    width:"50%",
    height: 60,
    alignItems:"center",
    justifyContent: "center",
  },
  fullScreen: {
    height: "100%",
    width: "100%",
    zIndex: 100,
    position: "absolute",
    backgroundColor: "red",
  }
});

Upvotes: 0

Views: 2389

Answers (1)

nithinpp
nithinpp

Reputation: 2025

You are using a functional component in the OP, which requires you to use useState Hooks for managing your state. If you were using a class component, you could use the setState approach. In both cases, React will take care of the re-rendering part.

If using class-based approach, mutating state like this.state.loadingScreen = false will not trigger a re-render of the UI. You will have to use the setState method instead.

Class-based approach

import React from "react";
import { Text, View, StyleSheet, TextInput, Alert, TouchableOpacity } from "react-native";
import AsyncStorage from '@react-native-community/async-storage';

export default class StartPage extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      loadingScreen: true,
      userInput: "",
      userInputName: ""
    };
  }

  readUserInput = (text) => {
    this.setState({
      userInput: text
    });
  }

  readUserInputName = (text) => {
    this.setState({
      userInputName: text
    });
  }

  storeData = async () => {
    const { userInput, userInputName } = this.state;
    try {
      AsyncStorage.setItem("RegistrationNumber", userInput);
      AsyncStorage.setItem("UserName", userInputName);
      this.props.navigation.navigate("GeneralInspection");
    }
    catch (e) {
      console.log(e);
    }
  }

  checkUserInput = () => {
    const { userInput, userInputName } = this.state;
    if (userInput.length < 1 && userInputName.length < 1)
      Alert.alert("Tarkista rekisterinumero ja nimi");
    else
      this.storeData();
  }

  renderElement = () => {
    if (this.state.loadingScreen)
      return (
        <View style={styles.fullScreen}>
          <TouchableOpacity style={styles.button} onPress={this.removeElement}>
            <Text style={{ fontSize: 20 }}>Change state to false</Text>
          </TouchableOpacity>
        </View>
      );
    return null;
  }

  removeElement = () => {
    this.setState({
      loadingScreen: false
    });
  }

  render() {
    return (
      <View style={styles.view} >
        { this.renderElement()}
        < Text style={styles.text} > Tiedot</Text>
        <TextInput
          style={styles.inputStyle}
          placeholder="Nimi"
          value={this.state.userInputName}
          onChangeText={this.readUserInputName}
          autoCapitalize="words"
        />
        <TextInput
          style={styles.inputStyle}
          placeholder="ABC-123"
          value={this.state.userInput}
          onChangeText={this.readUserInput}
          autoCapitalize="characters"
        />
        <TouchableOpacity style={styles.button} onPress={this.checkUserInput}>
          <Text style={{ fontSize: 20 }}>Aloita</Text>
        </TouchableOpacity>
      </View >
    );
  }
}

Functional component-based approach

import React, { useState } from "react";
import { Text, View, StyleSheet, TextInput, Alert, TouchableOpacity } from "react-native";
import AsyncStorage from '@react-native-community/async-storage';

export default function StartPage({ navigation }) {

  const [loadingScreen, setLoadingScreen] = useState(true);
  const [userInput, setUserInput] = useState('');
  const [userInputName, setUserInputName] = useState('');

  const readUserInput = (text) => {
    setUserInput(text);
  };

  const readUserInputName = (text) => {
    setUserInputName(text);
  };

  const storeData = async () => {
    try {
      AsyncStorage.setItem("RegistrationNumber", userInput);
      AsyncStorage.setItem("UserName", userInputName);
      navigation.navigate("GeneralInspection");
    }
    catch (e) {
      console.log(e);
    }
  };

  const checkUserInput = () => {
    if (userInput.length < 1 && userInputName.length < 1)
      Alert.alert("Tarkista rekisterinumero ja nimi");
    else
      storeData();
  };

  const renderElement = () => {
    if (loadingScreen)
      return (
        <View style={styles.fullScreen}>
          <TouchableOpacity style={styles.button} onPress={removeElement}>
            <Text style={{ fontSize: 20 }}>Change state to false</Text>
          </TouchableOpacity>
        </View>
      );
    return null;
  };

  const removeElement = () => {
    setLoadingScreen(false);
  };

  return (
    <View style={styles.view} >
      {renderElement()}
      < Text style={styles.text} > Tiedot</Text>
      <TextInput
        style={styles.inputStyle}
        placeholder="Nimi"
        value={userInputName}
        onChangeText={readUserInputName}
        autoCapitalize="words"
      />
      <TextInput
        style={styles.inputStyle}
        placeholder="ABC-123"
        value={userInput}
        onChangeText={readUserInput}
        autoCapitalize="characters"
      />
      <TouchableOpacity style={styles.button} onPress={checkUserInput}>
        <Text style={{ fontSize: 20 }}>Aloita</Text>
      </TouchableOpacity>
    </View >
  );
}

Upvotes: 1

Related Questions