Reputation: 18
I'm trying to update my old Todo List React app so that it uses hooks, and can't figure this out for the life of me. setTodos is successfully updating the state on load as shown in React developer tools, but the component doesn't re-render and a blank list of todos is displayed on the screen. Only when I add a Todo to the list does it re-render and all of the todos show up. Here's what I've got:
const App = () => {
// Todos are the only state in App
const [ todos, setTodos ] = useState([]);
// Fetches Todos from Firestore database on load
useEffect(() => {
const initialState = [];
dbTodos.get().then((snapshot) => {
snapshot.forEach((doc) => {
const currentTodo = doc.data();
currentTodo['id'] = doc.id;
initialState.push(currentTodo);
});
});
setTodos(initialState);
// eslint-disable-next-line
}, []);
// Add Todo
const addTodo = (title) => {
const newTodo = {
title : title,
completed : false
};
dbTodos.add(newTodo).then((doc) => {
newTodo['id'] = doc.id;
setTodos([ ...todos, newTodo ]);
});
};
Here is the code I used before implementing hooks:
componentDidMount() {
dbTodos.get().then((snapshot) => {
snapshot.forEach((doc) => {
const currentTodo = doc.data();
currentTodo['id'] = doc.id;
setState({ ...state, todos: [ ...state.todos, currentTodo ] });
});
});
};
Upvotes: 0
Views: 1236
Reputation: 1575
In useEffect you create empty array. Make a request. set empty array to state. When request was fulfilled you make changes in the array by ref. Therefore you see you array in dev tool and react does'n rerender you component.
const App = () => {
// Todos are the only state in App
const [ todos, setTodos ] = useState([]);
// Fetches Todos from Firestore database on load
useEffect(() => {
dbTodos.get().then((snapshot) => {
setTodos(snapshot.map((doc) => ({
id: doc.id,
...doc.data(),
})));
});
}, []);
// Add Todo
const addTodo = (title) => {
const newTodo = {
title : title,
completed : false
};
dbTodos.add(newTodo).then((doc) => {
newTodo['id'] = doc.id;
setTodos([ ...todos, newTodo ]);
});
};
Upvotes: 0
Reputation: 3998
When you setState in useEffect then you have to add setState in dependency. Instead of doing that use another arrow function and call it from useEffect.
use the code below
const App = () => {
// Todos are the only state in App
const [ todos, setTodos ] = useState([]);
// Fetches Todos from Firestore database on load
const fetchData = () => {
const initialState = [];
dbTodos.get().then((snapshot) => {
snapshot.forEach((doc) => {
const currentTodo = doc.data();
currentTodo['id'] = doc.id;
initialState.push(currentTodo);
});
});
setTodos(initialState);
}
useEffect(() => {
fetchData();
}, []);
// Add Todo
const addTodo = (title) => {
const newTodo = {
title : title,
completed : false
};
dbTodos.add(newTodo).then((doc) => {
newTodo['id'] = doc.id;
setTodos([ ...todos, newTodo ]);
});
};
Upvotes: 0
Reputation: 519
You should move your setTodos in UseEffect
useEffect(() => {
dbTodos.get().then((snapshot) => {
const initialState = []; // Also this you can move here
snapshot.forEach((doc) => {
const currentTodo = doc.data();
currentTodo['id'] = doc.id;
initialState.push(currentTodo);
});
setTodos(initialState); /// here
});
}, []);
Upvotes: 1