Re-render does not run when I call setState using react hooks.
Why is that?
When I press delete in todo app, the state value seems to have been updated, but redrawing does not run.
The state should have been updated with an array.
Here's the code.
//app.txs
constApp=()=>{
const [todo, setTodo] = React.useState<String[]>([])
// data storage
function handleAdd(e:any){
e.preventDefault()
setTodo(todo.concat(e.target.title.value))
e.target.title.value=""
}
// data deletion
commandRemove=(i:number)=>{
// Exclude first to i data in todo array
todo.splice(i,1)
setTodo (todo)
console.log(todo)
}
return(
<div>
<h1>React Todo App</h1>
<Form handleAdd={handleAdd}/>
<div></div>
<List todos={todo} handleRemove={handleRemove}/>
</div>
)
}
ReactDOM.render(<App/>, document.getElementById("app"))
// List.tsx
const List=(props:any)=>{
return(
<ul>
{props.todos.map(v:any, i:any)=>{
return<li key={i}>{v}<span style={{cursor:"pointer"}} onClick={()=>{props.handleRemove(i)}}>delete</span><</li>
})}
</ul>
)}
export default list
reactjs
UseStatehook avoids rendering children when updated with the same value.The "same value" here means Object.is (old state, new state)
is true
.
Source: https://ja.reactjs.org/docs/hooks-reference.html#bailing-out-of-a-state-update
Avoiding state updates
If the update is made with the same value as the current value, React avoids child renders or side effects and terminates the process (React uses comparison algorithm by Object.is).
If you look at the attached code, Object.is
compares true
because todo
directly changes todo
and passes the same todo
array instance to setTodo
.
Here's one solution.
consthandleRemove=(i:number)=>{
// Copy todo to create a new Array instance.
const newTodo = [...todo];
// where Object.is(todo, newTodo) === false.
// // https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/is
// Exclude the first data from the i-th in the newTodo array
newTodo.splice(i,1);
setTo (newTo);
}
Or you can write as follows:
consthandleRemove=(i:number)=>{
setTodo([...todo.slice(0,i),...todo.slice(i+1)]);
}
The concat
used in the handleAdd
returns a new Array instance, so the child is always rendered. If you change it directly using push
, it will not be re-rendered.
// Example not redrawn
consthandleAdd=(e)=>{
e.preventDefault();
todo.push(e.target.title.value);
setTodo(todo);
console.log(todo); // Output `todo` with new element added
e.target.title.value="";
};
They say they use immutability-helper to change the state array.
© 2024 OneMinuteCode. All rights reserved.