Home > Net >  Setting a setState to another state value in REACT
Setting a setState to another state value in REACT

Time:11-16

I have a React exercise about using states in class components. My solution works well when I code as below:

import React from "react";
import "./App.css";

class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = { lat: null, errorMessage: "" };

    window.navigator.geolocation.getCurrentPosition(
      (position) => this.setState({ lat: position.coords.latitude }),
      (err) => this.setState({ errorMessage: err.message })
    );
  }
  render() {
    if (this.state.lat && !this.state.errorMessage) {
      return <div>Latitude: {this.state.lat}</div>;
    } else if (!this.state.lat && this.state.errorMessage) {
      return <div>Erroe: {this.state.errorMessage}</div>;
    } else {
      return <div>Latitude: Loading...</div>;
    }
  }
}

export default App;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

But when I write it in another way, I get no error but no output

class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = { lat: null, errorMessage: "", message: "" };

    window.navigator.geolocation.getCurrentPosition(
      (position) => this.setState({ lat: position.coords.latitude }),
      (err) => this.setState({ errorMessage: err.message })
    );
    if (this.state.lat && !this.state.errorMessage) {
      this.setState({ message: "Latitude: "   this.state.lat });
    } else if (!this.state.lat && this.state.errorMessage) {
      this.setState({ message: "Error: "   this.state.errorMessage });
    } else {
      this.setState({ message: "Loading..." });
    }
  }
  render() {
    return <div> {this.state.message} </div>;
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

What is the problem by the second way?

CodePudding user response:

Two main issues:

1) setState is asynchronous

Even if the second problem didn't exist you cannot immediately introspect state immediately after calling setState; the state won't have been set yet.

2) getCurrentPosition is asynchronous

The callbacks for getCurrentPosition will execute at an indeterminate time in the future. The constructor continues to run, calling setState (based on current state, initialized to nulls and empty strings) before getCurrentPosition has likely called its callbacks.

(And there's zero reason to call setState in the constructor itself (as opposed to the callbacks to getCurrentPosition, which is fine), you can just set state directly as you do when you first initialize it.

CodePudding user response:

setState is async. this.state gets the new value in the next render but you're trying to use it right away.

There's nothing wrong with the first approach. If you want to store the message in the state, you can do that in the callbacks passed to getCurrentPosition and it is better to handle all that in ComponentDidMount rather than the constructor.

Edit agitated-pond-rv6u2

import { Component } from "react";
import "./styles.css";

export default class App extends Component {
  constructor(props) {
    super(props);

    this.state = { lat: null, errorMessage: "", message: "" };
  }

  componentDidMount() {
    window.navigator.geolocation.getCurrentPosition(
      (position) => {
        const lat = position.coords.latitude;
        this.setState({ lat, message: `Latitude: ${lat}` });
      },
      (err) =>
        this.setState({
          errorMessage: err.message,
          message: `Error: ${err.message}`
        })
    );
  }

  render() {
    return <div> {this.state.message} </div>;
  }
}
  • Related