0

I am new to programming and started learning React a few weeks ago. I am trying to create a weather app. I created a file called Weather.js where I the fetch api data that will be displayed. One of the inputs for the api link is lat/log. I decided to create another file called Button.js, where a user will enter their lat/long and submit it. Once submitted, that lat/long will get placed on the api link (in Weather.js), to fetch that person's weather forecast. I am able to console.log the button data in Button.js. How do I pass that data to Weather.js? I think I'm supposed to use props and/or a callback function, but I am at a loss on how to do it properly. Nothing has worked so far. Thank you for your help.

function Weather() {
  const [loading, setLoading] = React.useState(false)
  const [maxTemp, setMaxTemp] = React.useState([])
    
  React.useEffect(() => {
    setLoading(true) 
    fetch("https://api.openweathermap.org/data/2.5/onecall?lat=34.1030&lon=-118.4105&units=imperial&exclude=current,minutely,hourly,alerts&appid={api}")
      .then(res => res.json())
      .then((data) => {
      setLoading(false)
      setMaxTemp(data.daily[0].temp.max)
    })
  }, [])

  if(loading === true){
    return <div>Loading...</div>
  } else return( 
    <div>
    High: {Math.round(maxTemp)} <br />
    </div>
  )
}

ReactDOM.render(<Weather />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>

function Button(props) {
  
  const [lat, setLat] = React.useState([])

  const handleSubmit = (event) => {
    console.log(lat)
    event.preventDefault();
  }

  return(
    <form onSubmit={handleSubmit}>
    <input type="text" value={lat} onChange={e => setLat(e.target.value)} />
    <input type="submit" value="Submit" />
    </form>
  )
}

ReactDOM.render(<Button />, document.getElementById("root"));
<div id="root"></div><script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>

2
  • 1
    reactjs.org/docs/lifting-state-up.html Commented May 26, 2021 at 0:58
  • I see two render function calls, which normally you should have only one in an application. Also, I see no relationship between Weather and Button (I'm not going to question why it is named "Button" despite it is in fact a form). Are you sure this is exactly what your code looks like? If yes, then something is going really wrong. Commented May 26, 2021 at 1:13

2 Answers 2

1

so first you have to import the Button into your Weather file, then you have to pass a (setState) function into that button from the parent (weather.js) and then call that inside the button with passing the data:

and you don't have to pass the Button.js into React.DOM, we do that only 1 time for the most parent component.

Weather.js:

import Button from './button' // pass your correct paths

function Weather() {
  const [loading, setLoading] = React.useState(false)
  const [maxTemp, setMaxTemp] = React.useState([])
  const [coords, setCoords] = React.useState(null)
    
  React.useEffect(() => {
    if(!coords) return. // checking if no coords to skip calling API
    setLoading(true) 
    fetch(`https://api.openweathermap.org/data/2.5/onecall?lat=${coords.lat}&lon=${coords.long}&units=imperial&exclude=current,minutely,hourly,alerts&appid=${api}`)
      .then(res => res.json())
      .then((data) => {
      setLoading(false)
      setMaxTemp(data.daily[0].temp.max)
    })
  }, [coords]) // watching for coords change from button passed data

  if(loading === true){
    return <div>Loading...</div>
  } else return( 
    <div>
    High: {Math.round(maxTemp)} <br />
    <Button setCoords={setCoords} /> // here we pass the setState function into the button to get the data back once its called from inside
    </div>
  )
}

ReactDOM.render(<Weather />, document.getElementById("root"));

Button.js:

function Button(props) {
  const [lat, setLat] = useState("");
  const [long, setLong] = useState("");

  const handleSubmit = (event) => {
    console.log(lat)
    event.preventDefault();
    props.setCoords({ lat, long });  // here we call the function that is passed from parent and give it the data
  }

  return(
    <form onSubmit={handleSubmit}>
    <input
        placeholder="Lat"
        type="number"
        value={lat}
        onChange={(e) => setLat(e.target.value)}
      />
      <input
        placeholder="Long"
        type="number"
        value={long}
        onChange={(e) => setLong(e.target.value)}
      />
    <input type="submit" value="Submit" />
    </form>
  )
}
Sign up to request clarification or add additional context in comments.

Comments

0

You don't need to use two components. Simply add more state to your weather component.

You also don't need to use a form. Just create two input fields and a button.

Extract your effect code into a separate function. This way you can fire up API call on page load and on button click.

function Weather() {
  const [loading, setLoading] = React.useState(false);
  const [maxTemp, setMaxTemp] = React.useState([]);
  const [lat, setLat] = React.useState(34.103);
  const [lo, setLo] = React.useState(-118.4105);

  const apiCall = () => {
    setLoading(true);
    fetch(
      `https://api.openweathermap.org/data/2.5/onecall?lat=${lat}&lon=${lo}&units=imperial&exclude=current,minutely,hourly,alerts&appid={api}`
    )
      .then((res) => res.json())
      .then((data) => {
        setLoading(false);
        setMaxTemp(data.daily[0].temp.max);
      });
  };

  React.useEffect(() => {
    apiCall();
  }, []);

  return (
    <React.Fragment>
      <input
        placeholder="Latitude"
        type="range"
        step="0.5"
        max="90"
        min="-90"
        value={lat}
        onChange={(e) => setLat(e.target.value)}
      />
      <span>{lat}</span>
      <br/>
      <input
        placeholder="Longitude"
        type="range"
        step="0.5"
        max="180"
        min="-180"
        value={lo}
        onChange={(e) => setLo(e.target.value)}
      />
      <span>{lo}</span>
      <br/>
      <button onClick={apiCall}>Submit</button>
      {loading ? (
        <div>Loading...</div>
      ) : (
        <div>
          High: {Math.round(maxTemp)} <br />
        </div>
      )}
    </React.Fragment>
  );
}

ReactDOM.render(<Weather />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.