I'm trying to set a variable with a simple GET database call. The database call is returning the data correctly, but the variable remains undefined after every re-render. Code is below... the getMyThing()
function in the useState()
function is working correctly and returning the data I want.
import { getMyThing } from '../../utils/databaseCalls'
const MyComponent: React.FC = () => {
const { id } = useParams();
const [myThing, setMyThing] = useState(getMyThing(id));
useEffect(() => {
setMyThing(myThing)
}, [myThing]);
}
My thinking here was to use useState()
to set the initial state of the myThing
variable with the data returned from my database. I assume it's not immediately working since a database call is asynchronous, so I thought I could use useEffect()
to update the myThing
variable after the response of the database call completes, since that would trigger the useEffect()
function because I have the myThing
variable included as a dependency.
What am I missing here? Thanks!
EDIT: Thanks for the answers everyone, but I still can't get it to work by calling the getMyThing
function asynchronously inside useEffect()
. Is something wrong with my database call function? I guess it's not set up to a return a promise? Here's what that looks like:
export const getMyThing = (id) => {
axios.get('http://localhost:4000/thing/' + id)
.then(response => {
return(response.data);
})
.catch(function (error){
console.log(error);
})
}
If getMyThing
returns a Promise, the myThing
will be set to that Promise on the first render, and then myThing
will stay referring to that Promise. setMyThing(myThing)
just sets the state to the Promise again - it's superfluous.
Call the asynchronous method inside the effect hook instead:
const [myThing, setMyThing] = useState();
useEffect(() => {
getMyThing(id)
.then(setMyThing);
}, []);
Here, myThing
will start out undefined
, and will be then set to the result of the async call as soon as it resolves.
You can't set the initial state with a value obtained asynchronously because you can't have the value in time.
myThing
cannot both return the value you want and be asynchronous. Maybe it returns a promise that resolves to what you want.
Set an initial value with some default data. This might be null data (and later when you return some JSX from your component you can special case myThing === null
by, for example, returning a Loading message).
const [myThing, setMyThing] = useState(null);
Trigger the asynchronous call in useEffect
, much like you are doing now, but:
Thus:
useEffect(async () => {
const myNewThing = await getMyThing(id);
setMyThing(myNewThing)
}, [id]);
You should do all your side effects(fetching data, subscriptions and such) in useEffect
hooks and event handlers. Don't execute async logic in useState
as you just assign the promise itself to the variable and not the result of it. In any case, it is a bad practice and it won't work. You should either:
import { getMyThing } from '../../utils/databaseCalls'
const MyComponent: React.FC = () => {
const { id } = useParams();
const [myThing, setMyThing] = useState(null);
useEffect(() => {
const fetchData = async () => {
const result = await getMyThing(id);
setMyThing(result);
};
fetchData();
}, [id, getMyThing]);
}
Or if you don't want to introduce an async
function:
import { getMyThing } from '../../utils/databaseCalls'
const MyComponent: React.FC = () => {
const { id } = useParams();
const [myThing, setMyThing] = useState(null);
useEffect(() => {
getMyThing()
.then(result => setMyThing(result));
}, [id, getMyThing]);
}
Also, take note of the [id, getMyThing]
part as it is important. This is a dependency array determining when your useEffect
hooks are gonna execute/re-execute.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.