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="