I am trying to setState from useEffect but it comes back as undefined and I am unable to use it in other components. I am able to console log the state though and it displays the object fine. Thanks.
function App() {
const [tokens, setTokens] = useState();
console.log(tokens)
useEffect(() => {
async function init() {
await Moralis.initPlugins();
await Moralis.enable();
await listAvailableTokens();
}
init();
// token info from 1inch
const listAvailableTokens = async () => {
const result = await Moralis.Plugins.oneInch.getSupportedTokens({
chain: "eth", // The blockchain you want to use (eth/bsc/polygon)
});
const tokensObject = result.tokens;
console.log(tokensObject)
setTokens(tokensObject);
};
}, []);
Related
I get data from backend and set to my state in componentdidmount but value not set after log state
const [tasks, setTasks] = useState([]);
const getTasks = async () => {
const getTodoInformation = {
email: localStorage.getItem("tokenEmail"),
};
if (getTodoInformation.email) {
const response = await axios.post(
"http://localhost:9000/api/todo/get",
getTodoInformation
);
setTasks(response.data.data);
}
};
useEffect(() => {
getTasks();
console.log(tasks);
}, []);
My tasks is empty when i log it
So the title and the question itself are actually two questions.
React Hook useEffect has a missing dependency: 'tasks'. Either includes it or remove the dependency array
That's because you include a state (i.e. tasks) in the useEffect hook. And React is basically asking you, "Do you mean run console.log(tasks) every time tasks is updated?". Because what you are doing is run the useEffect hook once and only once.
And for your "actual" question
value not set after log state
In short, states are set in async manner in React. That means tasks is not necessary immediately updated right after you call setTasks. See #JBallin comment for details.
const [tasks, setTasks] = useState([]);
useEffect(() => {
setTimeout(async () => {
const getTodoInformation = {
email: localStorage.getItem("tokenEmail"),
};
if (getTodoInformation.email) {
const response = await axios.post(
"http://localhost:9000/api/todo/get",
getTodoInformation
);
setTasks(response.data.data);
}
}, 1000);
console.log(tasks);
}, []);
The main problem is that useEffect -> is a sync method, getTasks() is asynchronous, and useEffect only works once when your component mounts. Shortly speaking, you got your data from the backend after useEffect worked.
For example, if you will add one more useEffect
useEffect(() => {
console.log(tasks);
}, [tasks]);
You will see log, after your data will have changed.
You can use self-calling async function inside useEffect as shown here:
const [tasks, setTasks] = useState([]);
const getTasks = async () => {
const getTodoInformation = {
email: localStorage.getItem("tokenEmail"),
};
if (getTodoInformation.email) {
const response = await axios.post(
"http://localhost:9000/api/todo/get",
getTodoInformation
);
return response.data.data;
}
};
useEffect(() => {
(async () => {
const tasks = await getTasks();
setTasks(tasks);
})();
console.log(tasks);
}, [tasks]);
I'm trying to use a path param which I fetch from the URL path as an ID for an entity which I'm trying to fetch. I've created a custom data fetching hook which triggers when either the path of the passed params change. I'm use useParams from react router dom to get the ID of the book from the URL.
Below is the component code:
const BookDetails: FC<BookDetailsProps> = () => {
let { bookId } = useParams();
const { response, loading, error } = useApi(`/books/v1/volumes/${bookId}`);
return <Wrapper></Wrapper>;
};
export default BookDetails;
And below is my custom hook:
const useApi = (url: string, params = {}) => {
const [response, setResponse] = useState<AxiosResponse>();
const [error, setError] = useState<AxiosError>();
const [loading, setLoading] = useState(true);
const fetchData = useCallback(async () => {
setLoading(true);
try {
const result = await api(url, { params: params });
setResponse(result);
} catch (err: any) {
setError(err);
} finally {
setLoading(false);
}
}, [url, params]);
useEffect(() => {
fetchData();
}, [fetchData]);
return { response, error, loading, fetchData };
};
export default useApi;
Now when I use my API hook with a component which fetches data using the params object everything is fine. The component doesn't rerender and I just get the data, but for some reason when I use it with path params it goes off.
Does anyone know how I should proceed?
I think the issue is that when no params object is passed to the useApi hook
useApi(`/books/v1/volumes/${bookId}`)
The params argument is initialized to a default empty object value.
const useApi = (url: string, params = {}) => {
...
This causes the params variable to be a new object reference anytime the component rerenders for any reason and will retrigger the useEffect hook because fetchData will be a newly recomputed reference.
...
const fetchData = useCallback(async () => {
setLoading(true);
try {
const result = await api(url, { params: params });
setResponse(result);
} catch (err: any) {
setError(err);
} finally {
setLoading(false);
}
}, [url, params]); // <-- params new reference
useEffect(() => {
fetchData();
}, [fetchData]); // <-- fetchData becomes new reference
...
};
I suggest not initializing param and only provide a fallback value when calling the api function.
Example:
const useApi = (url: string, params) => { // <-- don't initialize
const [response, setResponse] = useState<AxiosResponse>();
const [error, setError] = useState<AxiosError>();
const [loading, setLoading] = useState(true);
const fetchData = useCallback(async () => {
setLoading(true);
try {
const result = await api(url, { params: params || {} }); // <-- provide fallback value
setResponse(result);
} catch (err: any) {
setError(err);
} finally {
setLoading(false);
}
}, [url, params]);
useEffect(() => {
fetchData();
}, [fetchData]);
return { response, error, loading, fetchData };
};
export default useApi;
This way params will be an undefined value from render to render and not change shallow reference equality each render.
params gets a new object instance on each call so the dependency array in the useCallback will never be the same between different calls (you haven't pass a params argument to your useApi) and therefore the useEffect will be triggered on each render casing a re-render, hence the infinite rerendering
My code (which seems to work ok) looks like this:
import { SplashScreen } from "#capacitor/splash-screen";
const MyComponent = () => {
const [data, setData] = useState()
useEffect(() => {
init()
}, [])
const init = async () => {
const response = await fetch("some_api")
setData(response.data)
await SplashScreen.hide()
}
return (<div>{JSON.stringify(data)}<div>)
}
But I'm wondering if it's better practive to move the await SplashScreen.hide() function call to a useEffect() with the data in the dependency array like this:
import { SplashScreen } from "#capacitor/splash-screen";
const MyComponent = () => {
const [data, setData] = useState()
useEffect(() => {
init()
}, [])
useEffect(() => {
if (data) {
await SplashScreen.hide()
}
}, [data])
const init = async () => {
const response = await fetch("some_api")
setData(response.data)
}
return (<div>{JSON.stringify(data)}<div>)
}
Note: SplashScreen.hide() returns a promise. Which way is better and why?
It depends if you want to call SplashScreen.hide() after the data has been set or not. In the first case it's not guaranteed that the call will be made after the data is set since setState works async. In the second case though, you are explicitly calling SplashScreen.hide() after the state has been updated.
Note, since you're not doing anything with the promise returned from SplashScreen.hide(), it's not necessary to call it with await.
I want to get real time bitcoin information but datas not coming. I get this error = React Hook useEffect has a missing dependency: 'coinData'. Either include it or remove the dependency array
const [coinData,setCoinData] = useState([]);
useEffect(() => {
const getData = async () =>{
const baseURL = "https://api.coingecko.com/api/v3/coins/bitcoin?tickers=true&market_data=true&community_data=true&developer_data=true&sparkline=true"
const response = await axios(baseURL)
setCoinData(response);
console.log(coinData)
}
getData();
}, []);
The error is because you're using coinData (state) inside useEffect.
If you add coindData to the dependencies array, you'll get an infinite loop.
To log the response use console.log(response), not console.log(coinData).
useEffect(() => {
const getData = async () =>{
const baseURL = "https://api.coingecko.com/api/v3/coins/bitcoin?tickers=true&market_data=true&community_data=true&developer_data=true&sparkline=true"
const response = await axios(baseURL)
setCoinData(response);
console.log(response);
}
getData();
}, []);
I have some issue. When I do to async fetch data (using axios for fetching) in the useEffect, and after I set responsed data to state, using a useState hook. And page render befor then I got response from server.
For demonstration this issue I have putted console.log for get current state, and I get 'undefined':
const [positions, setPositions] = useState([]);
useEffect(() => {
const fetchPositions = async () => {
const response = await EmployeeService.getEmployeePositions();
setPositions(response.data);
};
fetchPositions();
console.log('positions from state: ', positions); //undefined
}, []);
Method for fetching data from "EmployeeService":
getEmployeePositions(){
return axios.get(EMPLOYEE_API_BASE_URL + '/positions');
}
Thanks in advance, and best regards!
React needs to re-render to display the results.
Which means you need to capture the result on the subsequent re-render that is caused when you setState.
Move the console log outside of the useEffect
const [positions, setPositions] = useState([]);
useEffect(() => {
const fetchPositions = async () => {
const response = await EmployeeService.getEmployeePositions();
setPositions(response.data);
};
fetchPositions();
}, []);
console.log('positions from state: ', positions); // NOT UNDEFINED
React will always render once before you have data.
So you can catch it with a condition.
if (positions.length === 0) {
return null;
}
nothing wrong with your code, useEffect is always undefined because it read the first value of your rendered app.
To update state in useEffect put paramater on the array [] but in your case it will cause an infinity loop.
try logging inside the async function instead
const [positions, setPositions] = useState([]);
useEffect(() => {
const fetchPositions = async () => {
const response = await EmployeeService.getEmployeePositions();
setPositions(response.data);
console.log('data from response: ', response);
};
fetchPositions();
}, []);
or do it like this
const [positions, setPositions] = useState([]);
useEffect(() => {
const fetchPositions = async () => {
const response = await EmployeeService.getEmployeePositions();
setPositions(response.data);
console.log('data from response: ', response);
};
if((positions ?? []).length == 0){
fetchPositions();
console.log('this is position state before fetch~>',positions)
} else{
console.log('this is position state after fetch~>',positions)
}
}, [positions]);