Reputation: 2391
I have an asynchronous request in my useEffect. I notice that the component is rendered before the useEffect finishes.
import React, { useEffect } from 'react';
import axios from 'axios';
import './SearchWords.css';
const URL_REQUEST = 'http://myLink.com';
const SearchWords = (props) => {
const { setState } = props;
useEffect(async () => {
const data = {
sentence: props.sentence
};
await axios.post(`${URL_REQUEST}/getData`, { data })
.then((res) => {
setState(state =>({
...state,
search: res.data
}));
})
.catch((err) =>{
console.log(err);
})
}, []);
const render = () => {
if(props.search.length > 0) {
const linkMarkup = props.search.map((link) => (
<li key={link.id} className="link-list-item">
<a
href={link.link}
target="_blank"
rel="noopener noreferrer"
className="link-list-item-url"
onClick
>
{link.name}
</a>
</li>
));
return <ul className="link-list">{linkMarkup}</ul>;
} else {
return <p>No data found for: {props.sentence}</p>
}
}
return render()
}
export default SearchWords;
It is working, but it first shows No data found for: My Search String
. Then, it reloads and shows the <ul>
with the results. How can I avoid it to first show the "No data found" message.
Thanks
Upvotes: 7
Views: 12969
Reputation: 972
You want to track if your fetch
call resolved before rendering anything on the screen.
You could use React.useRef
to keep track of this state.
Upvotes: -4
Reputation: 735
It is working as expected read more about react lifecycle. To handle this case as you're using functional component use useState()
hook for API status.
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
// api call
// now after api call set setIsLoading(false);
},[]}
if(isLoading) {
// return show loading spinner
}
// else remaining code
Upvotes: 2
Reputation: 6607
That's how the useEffect
works: it will schedule a side effect to be run after the component has rendered.
From the docs:
What does useEffect do? By using this Hook, you tell React that your component needs to do something after render. React will remember the function you passed (we’ll refer to it as our “effect”), and call it later after performing the DOM updates. In this effect, we set the document title, but we could also perform data fetching or call some other imperative API.
What you can do is keep track of your effect status to show a loading indicator until it has finished.
import React, { useEffect } from 'react';
import axios from 'axios';
import './SearchWords.css';
const URL_REQUEST = 'http://myLink.com';
const SearchWords = (props) => {
const { setState } = props;
const [isLoading, setLoading] = React.useState(true);
useEffect(async () => {
const data = {
sentence: props.sentence
};
await axios.post(`${URL_REQUEST}/getData`, { data })
.then((res) => {
setState(state =>({
...state,
search: res.data
}));
setLoading(false);
})
.catch((err) =>{
console.log(err);
})
}, []);
const render = () => {
if(isLoading) return "Loading...";
if(props.search.length > 0) {
const linkMarkup = props.search.map((link) => (
<li key={link.id} className="link-list-item">
<a
href={link.link}
target="_blank"
rel="noopener noreferrer"
className="link-list-item-url"
onClick
>
{link.name}
</a>
</li>
));
return <ul className="link-list">{linkMarkup}</ul>;
} else {
return <p>No data found for: {props.sentence}</p>
}
}
return render()
}
export default SearchWords;
Upvotes: 4
Reputation: 371168
The simplest tweak would be to conditionally render that section of the JSX depending on whether the response has come back yet:
const [hasLoaded, setHasLoaded] = useState();
// ...
.then((res) => {
setState(state =>({
...state,
search: res.data
}));
setHasLoaded(true);
})
then change
return <p>No data found for: {props.sentence}</p>
to
return hasLoaded ? <p>No data found for: {props.sentence}</p> : <p>Loading...</p>;
Upvotes: 8