4

I have a simple weather app built using React. The App.js code includes the following in its return() statement:

<Routes>
  <Route exact path="/" element={<Form />} />
  <Route exact path="/weather/*" element={<Weather />} />
</Routes>

Form.jsx takes in user input, sets as State, and looks like:

export default function Form() {
  const [zipCode, setZipCode] = useState('');
  const getZip = (e) => {
    e.preventDefault();
    setZipCode(e.target[0].value);
  }

  return (
    <div>
      <form onSubmit={getZip}>
        <input id="zip" />
    <label htmlFor="zip">Your Zip Code</label>
        <Link to="/Weather" role="button" type="submit" className="btn btn-primary">Enter</Link>
      </form>
  )}

Weather.jsx is supposed to take that zip code and make an API call to get weather data:

function Weather() {
  const { zipCode } = useParams();
  const [forecastObj, setForecastObj] = useState({});

  useEffect(() => {
    fetch(`https://apiservice.com/forecast/loc=${zipCode}`)
}

The app is supposed to display Form.jsx, and switch to Weather.jsx once the zip code is submitted. When I input a zip code I get an error: GET https://apiservice.com/forecast/loc=undefined 400, so the zip code isn't being passed to Weather.jsx. Most of the resources I've found through googling seem to be for older versions (lots of this.state = state or similar). Other resources say to use useParams, which I have set up in Weather.jsx.

What am I missing?

ksav
  • 20,015
  • 6
  • 46
  • 66
  • Your two components are not related. You'll need some way to share state between them. One built-in option is the [Context API](https://reactjs.org/docs/context.html). Another would be a redux store – Phil Jun 15 '22 at 02:46

3 Answers3

1

If you setup your weather Route path to accept a parameter of zipCode, you can imperatively navigate there (instead of using Link) with the value captured in your Form component.

Then in your Weather component, you can access the route params with useParams.

import React, { useState } from "react";
import {
  BrowserRouter as Router,
  Routes,
  Route,
  useParams,
  useNavigate
} from "react-router-dom";

export default function App() {
  return (
    <Router>
      <Routes>
        <Route exact path="/" element={<Form />} />
        <Route exact path="/weather/:zipCode" element={<Weather />} />
      </Routes>
    </Router>
  );
}

function Form() {
  let navigate = useNavigate();
  const [zipCode, setZipCode] = useState("");

  const onSubmit = () => {
    navigate(`/weather/${zipCode}`);
  };

  return (
    <form onSubmit={onSubmit}>
      <label>
        Your Zip Code{" "}
        <input onChange={(e) => setZipCode(e.target.value)} />
      </label>
      <button>submit</button>
    </form>
  );
}

function Weather() {
  const { zipCode } = useParams();

  return `Weather for ${zipCode}`;
}

Edit blissful-snowflake-jklbxb

ksav
  • 20,015
  • 6
  • 46
  • 66
0
<Link to="/Weather" role="button" type="submit" className="btn btn-primary">Enter</Link>

Your Link does not pass any params to Weather.jsx and zipCode is also not static, so you cannot get any param data in Weather.

If you want to have params in Weather.jsx, you could replace Link with a primitive button element, and add useHistory - React Router v5 (or useNavigate - React Router v6) for the navigation instead.

React Router v5

import { useHistory } from 'react-router-dom';

export default function Form() {
  const [zipCode, setZipCode] = useState('');
  const history = useHistory();
  const getZip = (e) => {
    e.preventDefault();
    setZipCode(e.target[0].value);
    history.push(`/weather?zipCode=${e.target[0].value}`)
  }

  return (
    <div>
      <form onSubmit={getZip}>
        <input id="zip" />
    <label htmlFor="zip">Your Zip Code</label>
        <button type="submit" className="btn btn-primary">Enter</button>
      </form>
  )}

React Router v6

import { useNavigate } from 'react-router-dom';

export default function Form() {
  const [zipCode, setZipCode] = useState('');
  const navigate = useNavigate();
  const getZip = (e) => {
    e.preventDefault();
    setZipCode(e.target[0].value);
    navigate(`/weather?zipCode=${e.target[0].value}`);
  }

  return (
    <div>
      <form onSubmit={getZip}>
        <input id="zip" />
    <label htmlFor="zip">Your Zip Code</label>
        <button type="submit" className="btn btn-primary">Enter</button>
      </form>
  )}

If you want to have zipCode as a part of the URL implicitly, you can modify Route like below

<Routes>
  <Route exact path="/" element={<Form />} />
  <Route exact path="/weather/:zipCode" element={<Weather />} />
</Routes>

And modify Form accordingly

React Router v5

import { useHistory } from 'react-router-dom';

export default function Form() {
  const [zipCode, setZipCode] = useState('');
  const history = useHistory();
  const getZip = (e) => {
    e.preventDefault();
    setZipCode(e.target[0].value);
    history.push(`/weather/${e.target[0].value}`)
  }

  return (
    <div>
      <form onSubmit={getZip}>
        <input id="zip" />
    <label htmlFor="zip">Your Zip Code</label>
        <button type="submit" className="btn btn-primary">Enter</button>
      </form>
  )}

React Router v6

import { useNavigate } from 'react-router-dom';

export default function Form() {
  const [zipCode, setZipCode] = useState('');
  const navigate = useNavigate();
  const getZip = (e) => {
    e.preventDefault();
    setZipCode(e.target[0].value);
    navigate(`/weather/${e.target[0].value}`)
  }

  return (
    <div>
      <form onSubmit={getZip}>
        <input id="zip" />
    <label htmlFor="zip">Your Zip Code</label>
        <button type="submit" className="btn btn-primary">Enter</button>
      </form>
  )}
Nick Vu
  • 14,512
  • 4
  • 21
  • 31
0

To use router params you need to properly name them when you declare the Route path:

 <Route exact path="/weather/:zipCode" element={<Weather />} />

And then navigate to the proper route:

<Link to={`/weather/${zipCode}`} role="button" type="submit" className="btn btn-primary">Enter</Link>

Now you should be able to access the params properly when on the /weather/ route:

  const { zipCode } = useParams();
Cesare Polonara
  • 3,473
  • 1
  • 9
  • 19