Home > Software engineering >  React | Lifting the state up with event parameter gives error
React | Lifting the state up with event parameter gives error

Time:04-21

So I've been trying to pass through the result of an API call, but in the function that's returning the result I have a check to see if Enter was pressed or a click on the button. Now when I pass this function through while lifting the state up I get this error:

react-dom.development.js:11996 Uncaught TypeError: Cannot read properties of null (reading 'key')
at searchCity (Input.js:10:1)
at basicStateReducer (react-dom.development.js:16376:1)
at updateReducer (react-dom.development.js:16500:1)
at updateState (react-dom.development.js:16836:1)
at Object.useState (react-dom.development.js:17762:1)
at useState (react.development.js:1617:1)
at Weather (Weather.js:6:1)
at renderWithHooks (react-dom.development.js:16141:1)
at updateFunctionComponent (react-dom.development.js:20313:1)
at beginWork (react-dom.development.js:22356:1)

Anyone have any idea on how to fix this?

Here's my code: Input.js (child)

import { useRef } from "react";
import Data from '../config'

const Input = (props) => {

    const userInput = useRef();

    function searchCity(e) {

        if (e.key === 'Enter' || e.type === "click") {
            const inputValue = userInput.current.value;
            console.log(inputValue)

            fetch(Data.baseUrl1   inputValue)
            .then ((response) => response.json())
            .then (data => {
                const cityData = data.data[0]
                const lat = data.data[0].latitude
                const lon = data.data[0].longitude
                console.log(cityData, lat, lon)
                getWeatherData(lat, lon, cityData)
            })

        }
        
    }

    function getWeatherData(latitude, longitude, geoData) {
        fetch(Data.baseUrl2   latitude
              "&lon="   longitude   "&appid="   Data.key
              "&exclude=current,minutely,hourly&units=metric")
            .then((response) => response.json())
            .then(data => {
                return data;
            })
    }


    return (
        <div className="inputfield">
            <input ref={userInput} onKeyDown={searchCity} type="text" placeholder="City name here..."/>
            <button onClick={() => props.updateFetch(searchCity)}>Search</button>
        </div>
    )
}

export default Input;

Weather.js (parent)

import { useState } from "react";
import Input from "../components/Input";

const Weather = () => {

    const [fetchedData, setFetchedData] = useState(null);
    
    return (
        <Input updateFetch={fetchdata => setFetchedData(fetchdata)}/>
    )
}

export default Weather;

CodePudding user response:

Check this line -

Can not read property of null, reading key.

Which means e is your object but e.key is nothing and you are trying to read e.Key also means key is not the direct property of e object.

I guess it should be e.target.key

You can console log e and find all the properties it has and then filter out where the key property is.

The same goes for type.

CodePudding user response:

I believe you encounter this issue when you press the search button, which has problematic onClick behavior. When the search button is clicked, searchCity, which requires an e event parameter, immediately gets called, but you do not pass it in, so e is null, and you get the error Cannot read properties of null. You need to revise your onClick function to pass in the event: onClick={(e) => props.updateFetch(searchCity(e))}.

CodePudding user response:

tangled concerns

There's a lot of issues with the presented code. There is no return before getWeatherData so the .then returns undefined. Not that it really matters because your component has nowhere to store the city and weather data once it is fetched.

However, the biggest issue is that you are tangling API logic directly in your components. By isolating this code in separate modules, you make the components much easier to read/write and the API functions become reusable in other areas of your program. Note use of URL and URLSearchParams to avoid constructing URLs using strings and properly encode URL subcomponents -

// api/city.js

const BASE_URL = "https://api.citydata.com"

async function getCity(query) {
  const u = new URL("/get", BASE_URL)
  u.searchParams = new URLSearchParamas({ query })
  // https://api.citydata.com/get?query=foobar
  const {data} = await fetch(u).then(r => r.json())
  return data[0]
}

export { getCity }
// api/weather.js

const BASE_URL = "http://weatherdata.org"
const APP_ID = "2c50fb2b94ae720g"

async function getWeather(lat, lon) {
  const u = new URL("/json", BASE_URL)
  u.searchParams = new URLSearchParams({
    lat: lat,
    lon: lon,
    appid: APP_ID,
    exclude: "current,minutely,hourly",
    units: "metric"
  })
  // http://weatherdata.org/json?lat=1.23&lng=4.56&appid=2c50fb2b94ae720g&exclude=current,minutely,hourly&units=metric
  return fetch(u).then(r => r.json())
}

export { getWeather }

controlled components

Next I'd say there's some issues with your Search component. For one, there's no need to useRef in this situation. onChange should be used to make your input a controlled component and onKeyDown can be used to detect e.key == "Enter". Here's a functioning demo that you can run here in the browser to see how it's going to work -

// components/search.js

function Search() {
  const [query, setQuery] = React.useState("")
  const onSearch = e => {
    console.log("search for:", query)
  }
  return <div>
    <input
      value={query}
      onChange={e => setQuery(e.target.value)}
      onKeyDown={e => e.key == "Enter" && onSearch(e)}
      placeholder="enter a city name..."
    />
    <button type="button" onClick={onSearch} children="           
  • Related