I am having a strange problem where petData is initially being logged to the console as undefined and a few moments later populating the array of data. I assume this has to do with the time it takes to fetch the data and then send it through context. The problem is the app is crashing with the error "TypeError: can't access property "name", petData[0] is undefined" I tried using setTimeout but that does not help. Are there any solutions to this?
function SearchPage() {
const { petData } = useContext(PetDataContext);
console.log(petData[0].name);
const [filter, setFilter] = useState("");
const filteredData = useMemo(() => {
if (filter == "") return petData;
return petData.filter(
(item) =>
item.name.toLowerCase().includes(filter) ||
item.breed.toLowerCase().includes(filter)
);
}, [petData, filter]);
return (
<>
<SearchBar onSearch={(searchTerm) => setFilter(searchTerm)} />
<div className="d-flex flex-wrap sp-body">
<DogCardsDisplayed petData={filteredData} />
</div>
</>
);
}
export default SearchPage;
App.js
function App() {
const [petData, setPetData] = useState([]);
useEffect(() => {
axios
.get(`${BACK_PORT}/data`)
.then(function (response) {
setPetData(response.data);
})
.catch(function (error) {
console.log(error);
Swal.fire("Oops...", error.response.data, "error");
});
}, []);
console.log(petData);
return (
<div className="App">
<PetDataContext.Provider value={{ petData }}>
<BrowserRouter>
<NavBar />
<Switch>
<Route path="/" component={MainSignedOut} exact />
<Route path="/mainsignedin" component={MainSignedIn} />
<Route path="/searchpage" component={SearchPage} />
<Route path="/mypets" component={MyPets} />
<Route path="/admin" component={Admin} />
<Route exact path="/pets/:id" component={PetPage} />
<Route component={err404} />
</Switch>
</BrowserRouter>
</PetDataContext.Provider>
</div>
);
}
Context.js
import { createContext } from "react";
const PetDataContext = createContext([]);
export default PetDataContext;
Try this:
function App() {
const [petData, setPetData] = useState([]);
useEffect(() => {
axios
.get(`${BACK_PORT}/data`)
.then(function (response) {
setPetData(response.data);
})
.catch(function (error) {
console.log(error);
Swal.fire("Oops...", error.response.data, "error");
});
}, []);
console.log(petData);
return petData ? (
<div className="App">
<PetDataContext.Provider value={{ petData }}>
<BrowserRouter>
<NavBar />
<Switch>
<Route path="/" component={MainSignedOut} exact />
<Route path="/mainsignedin" component={MainSignedIn} />
<Route path="/searchpage" component={SearchPage} />
<Route path="/mypets" component={MyPets} />
<Route path="/admin" component={Admin} />
<Route exact path="/pets/:id" component={PetPage} />
<Route component={err404} />
</Switch>
</BrowserRouter>
</PetDataContext.Provider>
</div>
) : (
"Loading pets..."
);
}
The idea here is that since network calls are synchronous (they run concurrently and you can't tell when they execute), you simply watch the petData
state that would be updated and show the actual content when the petData
state holds a valid value. Until then, you can show some basic loader, like the kind you see on sites, maybe a spinner or something.