简体   繁体   中英

Problems with useState

Edited to include parent class...

I'm having problems changing state from a functional component using the useState hook. What am I doing wrong here? I've asterisked out my api key for this. locationOne and locationTwo are working great. Essentially this is a part of the app I'm making that takes a calculates the distance between two coordinates using the mapbox api.

import React, { useState, useEffect } from "react";
import { COORDS } from "./coords";

const CustomTrip = ({
  locationOne,
  locationTwo,
  onLocationOneChange,
  onLocationTwoChange
}) => {
  const [totalMiles, setTotalMiles] = useState(0);

  async function fetchDistance() {
    const res = await fetch(
      "https://api.mapbox.com/directions-matrix/v1/mapbox/driving/" +
        locationOne +
        ";" +
        locationTwo +
        "?sources=1&annotations=distance&access_token=****"
    );
    const mapBoxObject = await res.json();

    const meters = mapBoxObject.distances[0];
    const miles = parseInt(meters) * 0.00062137119;
    setTotalMiles(miles.toFixed(2));
    console.log(miles.toFixed(2));
  }

  useEffect(() => {
    fetchDistance();
  }, [locationOne, locationTwo]);

  return (
    <div>
      <center>
        <h1>Customize your trip</h1>
      </center>
      Select your starting point
      <select value={locationOne} onChange={onLocationOneChange}>
        {Object.entries(COORDS).map(([campus, longLatt]) => (
          <option key={campus} value={longLatt}>
            {campus}
          </option>
        ))}
      </select>
      Select your destination
      <select value={locationTwo} onChange={onLocationTwoChange}>
        {Object.entries(COORDS).map(([campus, longLatt]) => (
          <option key={campus} value={longLatt}>
            {campus}
          </option>
        ))}
      </select>
    </div>
  );
};

export default CustomTrip;

Here are the relevant bits of the parent class component:

class TippingPoint extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        totalMiles: 0,
        locationOne: '-97.4111604,35.4653761',
        locationTwo: '-73.778716,42.740913'       
      }



      this.onTotalMileChange.bind(this);
      this.onLocationOneChange.bind(this);
      this.onLocationTwoChange.bind(this);
    }  

    calculateTotals = () =>{ .... }



    onTotalMileChange = (event) => {
      this.setState({totalMiles: parseInt(event.target.value)},this.calculateTotals)    
    };

    onLocationOneChange = (event) => {
      this.setState({locationOne: event.target.value}, this.calculateTotals)
    }
    onLocationTwoChange = (event) => {
      this.setState({locationTwo: event.target.value}, this.calculateTotals)
    }

    render(){



                    <div>
                      <p>Trip Builder</p>

                          <CustomTrip totalMiles={this.state.totalMiles} locationOne={this.state.locationOne} 
                          locationTwo={this.state.locationTwo} onLocationOneChange={this.onLocationOneChange} 
                          onLocationTwoChange={this.onLocationTwoChange} onTotalMileChange={this.onTotalMileChange}/>

                    </div>


  export default TippingPoint;

Sounds more like you need to also pass a callback for CustomTrip to call in order to pass totalMiles back to a parent component. There is also now no need to temporarily store totalMiles in state, it can be passed directly in the callback.

const CustomTrip = ({
  locationOne,
  locationTwo,
  onLocationOneChange,
  onLocationTwoChange,
  onTotalMilesComputed
}) => {
  async function fetchDistance() {
    const res = await fetch(
      "https://api.mapbox.com/directions-matrix/v1/mapbox/driving/" +
        locationOne +
        ";" +
        locationTwo +
        "?sources=1&annotations=distance&access_token=****"
    );
    const mapBoxObject = await res.json();

    const meters = mapBoxObject.distances[0];
    const miles = parseInt(meters) * 0.00062137119;

    onTotalMilesComputed(miles.toFixed(2)); // <-- passed callback
    console.log(miles.toFixed(2));
  }

  useEffect(() => {
    fetchDistance();
  }, [locationOne, locationTwo]);

...

Parent component now just needs a handler for it. ( If parent is functional component make onTotalMilesComputed const and omit the this. )

onTotalMilesComputed = totalMilage => {
  // do something with totalMilage, like set/update state
}

...

render() {
  ...
  return (
    ...
    <CustomTrip
      // ...all other passed props, locations, etc...
      onTotalMilesComputed={this.onTotalMilesComputed}
    />
    ...
  );
}

Not sure what exactly is the issue faced by you. But as far as useState is concerned, try putting it after import statement, outside CustomTrip declaration.

not an answer, just a suggesgion:

This function doesn't feel as it belongs inside the Component. It's basically a pure function; takes some arguments and returns a result. And, at least I hope that, always the same result for the same arguments

async function fetchDistance(locationOne, locationTwo) {
  const res = await fetch(
    "https://api.mapbox.com/directions-matrix/v1/mapbox/driving/" +
      locationOne +
      ";" +
      locationTwo +
      "?sources=1&annotations=distance&access_token=****"
  );
  const mapBoxObject = await res.json();

  const meters = mapBoxObject.distances[0];
  const miles = parseInt(meters) * 0.00062137119;

  console.log(miles.toFixed(2));
  return miles.toFixed(2);
}

and inside the Component you just do:

useEffect(() => {
  fetchDistance(locationOne, locationTwo).then(setTotalMiles);
}, [locationOne, locationTwo]);

This may also help you move this logic into the parent Component, as I don't think it belongs in the one you show. The Component in your snippet is merely a controlled input to get the two locations. It has no state and no logic to do anything but get the two locations and forward them to the parent component.

So why should it contiain the logic to compute the distance of two props that are given to it, just to "return" the result back to the component that provided the props? Without even using that computed value in any way.

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