I am calling this api inside a context provider:
const url = 'http://127.0.0.1:8000/api/posts/'
const PostListContextProvider = (props) => {
const [posts, setPosts] = useState([])
useEffect(async () => {
const {data} = await axios.get(url);
setPosts(data)
}, [posts]);
return (
<PostListContext.Provider value={ posts }>
{ props.children }
</PostListContext.Provider>
);
}
Upon consuming the context using useContext, this error occurs:
react-dom.development.js:19710 Uncaught TypeError: destroy is not a function
What am I doing wrong?
ps.even though I am getting the error, I am still successfully fetching the data
useEffect should not be async
Do it like this:
useEffect(() => {
(async () => {
const {data} = await axios.get(url);
setPosts(data)
})()
}, [posts]);
useEffect is supposed to return a function if it returns something, since you are defining the callback function to useEffect as a promise, what it essentially returns is a promise which is not what it expects.
In order to use a async function within useEffect you would write it like
useEffect(() => {
async function myFn() {
const {data} = await axios.get(url);
setPosts(data)
}
myFn();
}, [posts]);
Related
I am new with react hooks, i'm trying to get info from an API but when i do the request i get 2 responses first an empty array and then the data of the API, why am i getting that empty array! , this is my first question, i'm sorry.
Thanks for helping me !
import {useState, useEffect} from 'react';
const getSlides = (API) => {
const[data,setData] = useState([]);
const getData = () =>
fetch(`${API}`)
.then((res) => res.json())
useEffect(() => {
getData().then((data) => setData(data))
},[])
return data
}
export default getSlides;
The useEffect() hook runs after the first render. Since you've initialized the data state with an empty array, the first render returns an empty array.
If you're component depends on data to render, you can always conditionally return null until your data is loaded.
Also, I recommend using an async function for api requests, it allows you to use the await keyword which makes your code easier to read. The only caveat, is that you cannot pass an async function to useEffect, instead define an async function inside your hook, and then call it.
import React, { useState, useEffect } from "react";
const API = "https://example.com/data";
const GetSlides = (props) => {
const [data, setData] = useState();
useEffect(() => {
async function getData() {
const request = fetch(API);
const response = await request;
const parsed = await response.json();
setData(parsed);
}
getData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (data === undefined) {
return null;
}
return <>data</>;
};
export default GetSlides;
Of course, you can still use Promise chaining if you desire.
useEffect(() => {
async function getData() {
await fetch(API)
.then((res) => res.json())
.then((data) => setData(data));
}
getData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
<GetSlides api="https://yay.com" />
react components need to be title case
import React, { useState, useEffect } from 'react'
const GetSlides = ({ api }) => {
const [data, setData] = useState(null)
const getData = async () =>
await fetch(`${api}`)
.then((res) => res.json())
.then((data) => setData(data))
useEffect(() => {
getData()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
console.log(data)
return <div>slides</div>
}
export default GetSlides
The effect callback function is called after the render of your component. (Just like componentDidMount) So during the first render phase, the data state has not been set yet.
You initialize your data with and empty array here:
const[data,setData] = useState([] <- empty array);
useEffect runs after your component is mounted, and then calls the API, that it might take a few seconds or minutes to retrieve the data, but you return the data right away before knowing if the API finished its call.
If you want to return the data after it has been retrieved from the API, you should declare and async method
const getSlides = async (API) => {
try {
const res = await fetch(API);
const data = await res.json();
return data;
} catch (e) {
throw new Error(e);
}
}
Note that it is not necessary hooks for this function
I am trying to use a react hook like useMemo or useEffect inside my functional component. The API call is async, and I think that may be what's causing the error.
Service file:
export const getData = (): wretch =>
fetch('/d/get_data')
.get()
.json();
Data formatting logic file:
import {getData} from './services';
export const formatData = (onClick, onSort) => {
// logic here - omitted
const formattedData = [];
return getData().then(res => {
res.forEach(
// more data formatting logic
)
return {formattedData: formattedData, originalData: res};
})};
Rendering file:
import {formatData} from './formatData';
const MyTable = () => {
useEffect(() => formatData({onClick: handleClick, onSort: handleSort}).then(res => setData(res)), []);
Error message:
You're correct about the part where you cant have async code in your useEffect. A workaroud for that is similar to what you're doing.
useEffect(() => {
async function myfunc(){
// Do async work
const response = await apiCall();
setData(response);
}
myFunc();
},[])
This might not answer your question, but it is a common pattern you might find useful :)
Please try this one.
const MyTable = () => {
useEffect(async () => {
const data = await formatData({onClick: handleClick, onSort: handleSort});
setData(data);
},
[]);
}
i've a (certainly) stupid problem.
my function getDataTbable is called in infinity loop I don't understand why... So the request is infinitive called.
export const TableResearch = ({setSelectedSuggestion,setImages}) => {
const [research, setResearch] = useState('');
const [suggestions, setSuggestions] = useState ([]);
const [table, setTable]= useState ([]);
const getDataTable = async () => {
const {data} = await jsonbin.get('/b/5f3d58e44d93991036184474');
setTable(data);
console.log(table)
};
getDataTable();
That is because the TableResearch function is called multiple times (every time this component is rendered). If you want to run a function only when the component is mounted, you'll have to use useEffect. Here is an example:
useEffect(() => {
const {data} = await jsonbin.get('/b/5f3d58e44d93991036184474');
setTable(data);
}, []);
The second parameter [] passed to useEffect is important. It makes the function run only once.
You can learn more about useEffect from HERE
The component re-renders every time you change it's state (setTable).
You should use useEffect to only execute your function the first time it renders.
Also you might encounter this warning:
Warning: Can't perform a React state update on an unmounted component.
if the async call finishes after component has unmounted. To account for that, write useEffect like this:
// outside component
const getDataTable = async () => {
const { data } = await jsonbin.get("/b/5f3d58e44d93991036184474");
return data;
};
useEffect(() => {
let mounted = true;
getDataTable()
.then(data => {
if (!mounted) return;
setTable(data);
});
return () => {
mounted = false;
};
}, []);
You can try this
useEffect(() => {
const getDataTable = async () => {
const { data } = await jsonbin.get("/b/5f3d58e44d93991036184474");
setTable(data);
console.log(table);
};
getDataTable();
}, []); // [] makes it runs once
Yes, getDataTable(); is being executed everytime the view is rendered, including when the data is returned.
Try wrapping getDataTable() like this:
if (!table.length) {
getDataTable()
}
But you will need to handle the case for if the requests returns no results, in which case it will still run infinitely:
const [table, setTable]= useState([]);
const [loading, setLoading]= useState();
const getDataTable = async () => {
setLoading(true);
const {data} = await jsonbin.get('/b/5f3d58e44d93991036184474');
setTable(data);
setLoading(false);
};
if (typeof loading === 'undefined' && !table.length) {
getDataTable();
}
I am trying to import a function that fetches data from an api in a file (.src/api/index.js) to my App.js (.src/App.js).
.src/api/index.js
import axios from 'axios';
const url = 'https://covid19.mathdro.id/api';
export const fetchData = async () => {
try {
const res = await axios.get(url);
return res;
} catch (error) {}
};
.src/App.js
import React, { useEffect } from 'react';
import { fetchData } from './api';
const App = () => {
useEffect(() => {
const data = fetchData();
console.log(data);
}, []);
return <div></div>;
};
export default App;
};
I am getting a Promise{<pending>} in my console when I run this but I am trying to get the values in the object.
fetchData() is an async function, and you need to await it like so:
const data = await fetchData();
Then, the useEffect must also be an async function:
useEffect(async () => {
const data = await fetchData();
console.log(data);
}, []);
You are not waiting for promise to resolve. use await or .then. If you wanna use await, make callback function of useEffect async function.
const App = () => {
useEffect(async () => {
const data = await fetchData();
console.log(data);
}, []);
return <div></div>;
};
Other approach is to use .then.
const App = () => {
useEffect(async () => {
const data = fetchData().then((data) => console.log(data));
}, []);
return <div></div>;
};
I have the following React functional component with useState and useEffect hooks:
import React, { useState, useEffect } from 'react';
import { requestInfo } from './helpers/helpers';
const App = () => {
const [people, setPeople] = useState([]);
useEffect(() => {
requestInfo('people', '82')
.then(results => setPeople(results))
}, []);
return (
<p>We have {people.length} people in this game.</p>
);
}
export default App;
In helpers.js I have this function:
export const requestInfo = (resource, quantity) => {
fetch(`https://swapi.dev/api/${resource}/?results=${quantity}`)
.then(response => response.json)
.then(data => data.results)
}
I don't have much experience of writing asynchronous functions that call APIs, but I've looked at other Q&As on this website that said in situations like this I need to have the helper function return a promise, then have the code that calls the helper function do something with the outcome of the promise, in this case pass it to the setPeople hook state setter function.
I'm currently getting the error TypeError: Cannot read property 'then' of undefined from within my call to useEffect.
I think the endpoint is correct because this command works and returns the expected data when run in my terminal:
curl https://swapi.dev/api/people/\?results\=82
I'd be very grateful if someone can show me how to modify one or both of my functions to make this work.
a few issues - one in useEffect you need to await the response from the requestInfo call so:
useEffect(() => {
const getAsyncInfo = async () => {
const res = await requestInfo('people', '82')
setPeople(res)
}
getAsyncInfo()
}, [])
next make sure you are returning the fetch from requestInfo and it's json() as a function - requestInfo can use async/await as well such as:
export const requestInfo = async(resource, quantity) => {
const res = await fetch(`https://swapi.dev/api/${resource}/?results=${quantity}`)
const json = await res.json()
return json.results
}
Your function requestInfo is currently not returning anything (hence the error message). You either need to get rid of the {} after the arrow, or you need to return fetch(...).then(...) etc.
export const requestInfo = (resource, quantity) =>
fetch(`https://swapi.dev/api/${resource}/?results=${quantity}`)
.then(response => response.json)
.then(data => data.results)
export const requestInfo = (resource, quantity) => {
return fetch(`https://swapi.dev/api/${resource}/?results=${quantity}`)
.then(response => response.json)
.then(data => data.results)
}