I’m trying to fetch city data using the OpenCage API and display the results in a list using React and Chakra UI. However, I’m getting the following error: Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. I use page routing. This is my code:
import NextLink from "next/link";
import { useState } from "react";
export default function Search() {
const [searchTerm, setSearchTerm] = useState("");
const [results, setResults] = useState([]);
const handleSearch = async () => {
if (!searchTerm.trim()) return;
const response = await fetch(
`https://api.opencagedata.com/geocode/v1/json?q=${searchTerm}&key=YOUR_API_KEY`
);
const data = await response.json();
const cityResults = data.results.map((result) => ({
id: `${result.geometry.lat},${result.geometry.lng}`,
name: result.formatted,
lat: result.geometry.lat,
lng: result.geometry.lng,
}));
setResults(cityResults);
};
return (
<Box>
<Text>Search for a city</Text>
<Input value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} />
<Button onClick={handleSearch}>Search</Button>
<List>
{results.map((city) => (
<ListItem key={city.id}>
<NextLink href={`/city/${city.id}`} passHref>
<Link>{city.name}</Link>
</NextLink>
</ListItem>
))}
</List>
</Box>
);
}
When I attempt to render the results, I get the error Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
I’ve tried using forEach() instead of map(), checked if I’m importing the components correctly, and ensured that the data returned from the API is in the expected format, but the issue persists.
I’ve checked the imports of NextLink, List, and ListItem.
Is working fine showing only one result with the following code:
const [searchTerm, setSearchTerm] = useState("");
const [result, setResult] = useState(null);
.....
const firstCity = data.results[0]; // Take only the first result
const city = {
id: `${firstCity.geometry.lat},${firstCity.geometry.lng}`,
name: firstCity.formatted,
lat: firstCity.geometry.lat,
lng: firstCity.geometry.lng,
};
setResult(city);
console.log("Result:", city);
} catch (error) {
console.error("Error fetching data:", error);
alert("Something went wrong. Please try again.");
}
};
return (
<>
....
<Box mt={8} px={4}>
{result ? (
<Box>
<Text fontSize="lg">
<NextLink href={`/city/${result.id}`} passHref>
<Link color="teal.500">
{result.name} (Lat: {result.lat}, Lng: {result.lng})
</Link>
</NextLink>
</Text>
</Box>
) : (
<Text>No results found</Text>
)}
</Box>
</>
);
}
Any help is greatly appreciated!
2
Answers
You should always perform a check before rendering:
This is because, at the moment you try to map over
results
, your asynchronous fetch might not have returned the data yet.I advise implementing a loader or using a Skeleton component to display while
results.length
is still empty.is your indication that one of the element you are trying to render is not a string but an object, the best way to tackle this would be
console.log(result)
and make sure the output of the result is in a syntax that is compatible of how you are rendering it i.e.,{id, name, lat, long}
are flat object ofresult
and that there is no further nesting.and make sure to validate if the data is available when rendering, else it will fail when there is no data.
city?.name
.