So I have a component that has a 'posts' variable passed in, which is managed by useState, and inside my App component I have some child components which use that variable:
function App() {
const [posts, updatePosts] = useState([
{
title: "Cut the Grass",
description: "Get it done before 4pm",
},
{
title: "Purchase LOTR: DVD box set",
description: "Extended Edition."
}
])
return (
<div>
<UserBar loggedInFlag = {userIsLoggedIn}/>
<br /><br /><hr /><br />
<CreateTodo todoList={posts} updatePosts = { () => updatePosts } />
<input type="submit" value="test" onClick={() => console.log(posts)}/>
<TodoList items={posts}/>
</div>
)
}
export default App;
There is logic inside my CreateTodo component that grabs fields for a new post, adds it to the posts
array, and successfully updates the posts
variable; I can test this with my "test" button on the second line there, which always prints out the correct todo list to the console, and it contains my new post. So this shows me that the update of posts
inside the <CreateTodo>
bubbles up to my App component just fine.
However, the <TodoList >
component, which uses the same posts
variable as above, does not re-render when the variable is updated. That component only displays the todolist (with the new value) when I change some code in the IDE, save, and then the page refreshes with the new list. How do I fix my useState so that whenever the post
value is updated, it will automatically rerender the <TodoList >
component?
For reference, here is how that component looks.
export default function TodoList({ items}) {
return (
<div>
{items.map( (todo, i) => <Todo {...todo} title={todo.title} description={todo.description} key={'post-' i} />)}
</div>
)
and, here is how the CreateTodo component looks:
import React from 'react'
export default function CreateTodo ({todoList, updatePosts}) {
return (
<form onSubmit={e => e.preventDefault()} style={{borderStyle:"solid"}}>
<div>
<br/>
<label htmlFor="create-title">Title:</label>
<br/>
<input type="text" id="create-title" />
<br/>
<label htmlFor="create-title">Description (optional):</label>
<br/>
<input type="text" id="create-description" />
<br/>
<br/>
</div>
<input type="submit" value="Create" onClick={() => addTodo({todoList, updatePosts})}/>
</form>
)
}
function addTodo({todoList, updatePosts}){
let newTodo = {
title : document.getElementById("create-title").value,
description : document.getElementById("create-description").value,
dateCreated : Date.now(),
completed : false,
dateCompleted : null
}
todoList.push(newTodo);
updatePosts(todoList);
document.getElementById("create-title").value = "";
document.getElementById("create-description").value = "";
// alert(`"${newTodo.title}" has been added to list`);
}
CodePudding user response:
First, you need to pass the updatePosts
function directly to your component:
<CreateTodo todoList={posts} updatePosts={updatePosts} />
Second, in your addTodo
function, you're mutating the state object directly:
// Don't do this
todoList.push(newTodo);
All you need to do is to call updatePosts
with the updated list:
updatePosts([
...todoList,
newTodo,
]);
CodePudding user response:
You need to add () in the updatePosts prop
updatePosts={() => updatePosts()} or just updatePosts={updatePosts}