Reputation: 119
I have problem with async function. I need track.user
in another function but my func getTracks() async. I don't have clue how can i get this.
const Player = ({trackUrl, index, cover, id}) => {
const [track, setTrack] = useState({})
const [user, setUser] = useState({})
useEffect(() => {
const getTracks = async () => {
await httpClient.get(`/track/${id}`)
.then((response) => {
setTrack(response.data);
})
}
getTracks();
getUser() // track.user undefined
}, [])
const getUser = async() => {
await httpClient.get(`/profile/${track.user}/`)
.then((response) => {
setUser(response.data);
})
}
}
Upvotes: 3
Views: 2662
Reputation: 2059
I would declare both functions at the beginning of the component (you can later optimise them with useCallback
but it's not that important in this phase).
const getTracks = async () => {
await httpClient.get(`/track/${id}`)
.then((response) => {
setTrack(response.data);
})
}
const getUser = async() => {
await httpClient.get(`/profile/${track.user}/`)
.then((response) => {
setUser(response.data);
})
}
I would then call an async
function inside the useEffect
hook. There are a couple of ways of doing it: you can either declare an async
function in the useEffect
hook and call it immediately, or you can call an anonymous async
function. I prefer the latter for brevity, so here it is:
useEffect(() => {
(async () => {
await getTracks();
getUser();
})();
}, []);
Now when you call getUser
you should be sure that getTracks
has already set the track
variable.
Here is the complete component:
const Player = ({trackUrl, index, cover, id}) => {
const [track, setTrack] = useState({})
const [user, setUser] = useState({})
const getTracks = async () => {
await httpClient.get(`/track/${id}`)
.then((response) => {
setTrack(response.data);
})
}
const getUser = async() => {
await httpClient.get(`/profile/${track.user}/`)
.then((response) => {
setUser(response.data);
})
}
useEffect(() => {
(async () => {
await getTracks();
getUser();
})();
}, []);
}
Following Noel's comments and linked sandbox, I figured out that my answer wasn't working. The reason why it wasn't working is that the track
variable was't available right after the getTrack()
hook execution: it would have been available on the subsequent render.
My solution is to add a second useEffect
hook that's executed every time the track
variable changes. I have created two solutions with jsonplaceholder endpoints, one (see here) which preserves the most of the original solution but adds complexity, and another one (here) which simplifies a lot the code by decoupling the two methods from the setTrack
and setUser
hooks.
I'll paste here the simpler one, adapted to the OP requests.
export default function Player({ trackUrl, index, cover, id }) {
const [track, setTrack] = useState({});
const [user, setUser] = useState({});
const getTracks = async () => {
// only return the value of the call
return await httpClient.get(`/track/${id}`);
};
const getUser = async (track) => {
// take track as a parameter and call the endpoint
console.log(track, track.id, 'test');
return await httpClient.get(`profile/${track.user}`);
};
useEffect(() => {
(async () => {
const trackResult = await getTracks();
// we call setTrack outside of `getTracks`
setTrack(trackResult);
})();
}, []);
useEffect(() => {
(async () => {
if (track && Object.entries(track).length > 0) {
// we only call `getUser` if we are sure that track has at least one entry
const userResult = await getUser(track);
console.log(userResult);
setUser(userResult);
}
})();
}, [track]);
return (
<div className="App">{user && user.id ? user.id : "Not computed"}</div>
);
}
Upvotes: 2
Reputation: 63524
You shouldn't be mixing then
s with async/await
. You should be using another useEffect
that watches out for changes in the track
state and then calls getUser
with that new data.
function Player(props) {
const { trackUrl, index, cover, id } = props;
const [ track, setTrack ] = useState({});
const [ user, setUser ] = useState({});
async function getTracks(endpoint) {
const response = await httpClient.get(endpoint);
const data = await response.json();
setTrack(data);
}
async function getUser(endpoint) {
const response = await httpClient.get(endpoint);
const data = await response.json();
setUser(data);
}
useEffect(() => {
if (id) getTracks(`/track/${id}`);
}, []);
useEffect(() => {
if (track.user) getUser(`/profile/${track.user}`);
}, [track]);
}
Upvotes: 1
Reputation: 2695
You can move the second request to the then
block of the dependent first request,i.e., getTracks
.
Also, you shouldn't mix then and await.
useEffect(() => {
const getTracks = () => {
httpClient.get(`/track/${id}`)
.then((response) => {
setTrack(response.data);
httpClient.get(`/profile/${response.data.user}/`)
.then((response) => {
setUser(response.data);
})
})
}
getTracks();
}, [])
Upvotes: 2