Потому что компоненты никого сами по себе не ждут и рендерятся
синхронно, т.е. "прямо сейчас", а ответ от сервера приходит
асинхронно, т.е. "когда-нибудь потом". Более того, хук
useEffect
сам по себе
только запускается уже
после рендера.
Варианта два:
1. Задать стейт по умолчанию:
const [todos, setTodos] = useState([]);
.
Сначала покажет пустой список, потом подгруженный.
2. Не рендерить компонент, пока нет нужных для него данных:
{todos ? <TodoList todos={todos}/> : <LoadingComponent/>}
Сначала покажет какой-нить спиннер, потом подгруженный список.