简体   繁体   中英

Setting a setState to another state value in REACT

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>

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>

What is the problem by the second way?

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

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.

编辑 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>;
  }
}

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM