I am trying to pass some data from one page to the other in react using the useNavigate and useLocation hook.The data is transferring fine and i am recieving it in the next component as shown below:
// sending component
const handleNavigate = () => {
navigate('/updateBlogPost', { state: { blogPost } });
};
//recieving component
const location = useLocation();
const [title, setTitle] = useState(location.state.blogPost.title);
const [description, setDescription] = useState(location.state.blogPost.description);
const [categories, setCategories] = useState(location.state.blogPost.categories);
const [content, setContent] = useState(location.state.blogPost.content);
Now I am facing the problem with the category array because it becomes undefined when the page first renders and i get the following error:
Uncaught TypeError: Cannot read properties of undefined (reading 'map')
I can get rid of the error using optional chaining but how to get the data of the array when it is available.
I tried setting it in the use effect but it does not seems to work can any one suggest some better way?
use useEffect to get data when its available based on dependencies of "location" changes everytime it will fire it after every completed render.
const [categories, setCategories] = useState([]);
useEffect(() => {
if(location.state.blogPost.categories)
setCategories(location.state.blogPost.categories)
},[location])
Related
I have a sidebar which sets a category Id on click and shows product information based on this Id.
In order to display each product's details, I make an api post call using axios in a useEffect() hook and setData with useState hook.
However, when I try to console.log the data, I get unreliable data back, that is, some undefined and some data.
import { useState, useEffect, useCallback, useMemo } from "react";
import axios from "axios";
import ProductCard from "../Product-cards/_medium-product-card";
const ProductDisplay = ({ subCategorySkus, categoryId, allSkus }) => {
const [data, setData] = useState();
const [isLoading, setIsLoading] = useState();
const apiRequest = { product_skus: allSkus };
const productData = useProductData(apiRequest);
useEffect(() => {
const fetchData = async () => {
try {
const response = await axios.post("/products", apiRequest);
setData(response.data);
setIsLoading(false);
} catch (err) {
console.log(err.message);
}
};
fetchData();
}, [categoryId]);
console.log(data);
return isLoading ? (
" data is loading ..."
) : (
<div className="product-display">
<h3>Child component Id: {categoryId}</h3>
<ProductCard categoryId={categoryId} />
</div>
);
};
export default ProductDisplay;
You can see here what I get in the console:
.
I have tried adding an if condition, but I am not sure that addresses the issue. I have tried with different dependencies in the useEffect. But I feel there is something I am not understanding.
The api is sending the information correctly, but I get several responses in the console.log and some of them come in as undefined.
This means that I can do a data.map because it crashes the component.
Any help?
UPDATE:
#moonwave99 provided the answer (see below). For the benefit of others who may browse through this question and to clarify what the solution entails, I am including an image below, showing where I changed my code. I think that most importantly, I had not initialised my useState() to an empty array, like this: useState([]).
Any further clarification of the issues here by anyone who knows more, is welcomed. As this is only a quick fix without enough context to understand the logic.
I understand that there is a initial state of the data i.e 'null' before fetching the data from API call. But when API is called the data should update , instead log shows me 2 null values before logging actual data.
const [error, setError] = useState(null);
const [isLoaded, setIsLoaded] = useState(false);
const [data, setData] = useState(null);
const { user } = isAuthenticated();
useEffect(() => {
getUser(user.userId)
.then()
.then(
(result) => {
setIsLoaded(true);
setData(result);
},
(error) => {
setIsLoaded(true);
setError(error);
}
)
}, [])
console.log(data)
Why am I getting these null values before actual data?
How to avoid these null values?
Those initial null values are causing errors if I try to show that value on page.
Due to StrictMode (calling render function twice) you get two logs of null in the console on the first render.
Also, you have two state changes that aren't batched (setIsLoaded, setData), React doesn't batch state changes in promises.
Therefore you have log (null) on the first render, another one (null) on setIsLoaded, and finally the data from setData.
And because you use useEffect, the callback will be called after the first render.
I keep this error Undefined is not an object(evaluating ....), I find it funny, because I get the object when i make a correction and it refreshes, but when i open a app at first, i get the error, So i delete the part that has the error then the app loads, i then bring back the delete part, it will show correctly. Please help, Im just learning react.
Im not sure but i think there is a method that makes my app wait when it fetches the data, then its loads, rather than just load at first nonetheless...
This is my code please.
function ExploreScreen() {
const getListingsApi = useApi(client.get(endpoint));
useEffect(() => {
getListingsApi.request();
}, []);
return ({getListingsApi && <Text>{getListingsApi.data[0].latitude}</Text>}) -this is the part i delete and bring back to ake it display.
I removed the parts that are working. This is just a brief version of the code, below is my useAPI hook
import { useState } from "react";
export default useApi = (apiFunc) => {
const [data, setData] = useState([]);
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
const request = async (...args) => {
setLoading(true);
const response = await apiFunc(...args);
setLoading(false);
setError(!response.ok);
setData(response.data);
return response;
};
return { data, error, loading, request };
};
It's hard without a reproducible example, but I would guess that data[0] might be the culprit:
return ({getListingsApi && <Text>{getListingsApi.data[0].latitude}</Text>})
The collection is being initialized to [] (in the hook via const [data, setData] = useState([]);) which means it won't have a first element.
This would explain why adding it later, would not cause an exception, since at that point, the data will already be loaded and the array will have been initialized.
Consider trying something like this:
return (
{getListingsApi && getListingsApi.data.length > 0 && (
<Text>{getListingsApi.data[0].latitude}</Text>
)}
)
I watched a Youtube video and I made my own recipe app. I'm a beginner at React.js and I've been solving this problem for about 2 days. Seems that i cant pass the value of my state to useEffect hook. Here's an example of my code. The error says
"React Hook useEffect has a missing dependency: 'query'. Either include it or remove the dependency array" and everytime I typed in the input box it triggers the useEffect hook. Thank you and your help is very much appreciated.
const [recipes, setRecipes] = useState([]);
const [search, setSearch] = useState('');
const [query, setQuery] = useState('steak');
const updateSearch = e => {
setSearch(e.target.value);
console.log(search)
}
const getSearch = e => {
e.preventDefault();
setQuery(search);
}
useEffect(() => { // error Is from the Query variable
const GetRecipe = async () => {
const APP_ID = "3834705e";
const APP_KEY = "c23e9514f82c2440abf54b21edd4c3dc";
const res = await fetch(`https://api.edamam.com/search?q=${query}&app_id=${APP_ID}&app_key=${APP_KEY}`);
const data = await res.json();
setRecipes(data.hits);
}
GetRecipe();
},[getSearch]) //this triggers everytime I typed in the input box which is not it is supposed to
return(
<div className='recipelist'>
<form onSubmit={getSearch}>
<input type="search" onChange={updateSearch}/>
<button type='submit'>submit</button>
</form>
As the error tells you, when using a useEffect hook, that hook can receive two arguments, the first one is the handler effect and the second one is an array containing all dependencies that effect will use, so as you are using the query state into the http url, you need to pass that dependency into the array, so could be something like this.
useEffect(() => { // error Is from the Query variable
const GetRecipe = async () => {
const APP_ID = "3834705e";
const APP_KEY = "c23e9514f82c2440abf54b21edd4c3dc";
const res = await fetch(`https://api.edamam.com/search?q=${query}&app_id=${APP_ID}&app_key=${APP_KEY}`);
const data = await res.json();
setRecipes(data.hits);
}
GetRecipe();
},[getSearch, query])
so what is actually doing the array dependency, as React docs says, array dependency it's used to check if the effect should execute again based on its dependencies, so in your code everything you type something, getSearch method is re-creating again and again in memory, so it will check the last getSearch function that it took and compare it with the new ones, so it will check as equally checker like fn1 === fn2, so due to both function are exactly the same, both keeps different space in memory, so both are different objects, check this docs to understand the concept.
Here you have the react docs too
I want to update my state hook with useEffect. Somehow it gives me an error and brokes my app.
Console.log gives right answer in console but somehow react doesn't want to recognize setCountries as a state hook function. What I am doing wrong here?
import React, { useState, useEffect } from 'react'
import axios from 'axios'
var host = 'https://restcountries.eu/rest/v2/all'
const App = () => {
const { countries, setCountries } = useState([])
useEffect(() => {
axios
.get(host)
.then(response => {
console.log(response.data)
setCountries(response.data)
})
})
return (
<div>
</div>
)
}
export default App
And error I am getting in browser
Unhandled Rejection (TypeError): setCountries is not a function
error in console:
Uncaught (in promise) TypeError: setCountries is not a function
at App.js:14
Two things: useState returns an array with two elements, and an array does not have a countries or setCountries key to destructure for. You could do this:
const { 0: countries, 1: setCountries } = useState([]);
or just
const [countries, setCountries] = useState([]);
Secondly, useEffect(..) without any second argument will cause the effect to be triggered on every rerender, so your component will get stuck in an update loop. Use an empty array as its second argument to only trigger the effect on load.