Search code examples
reactjsreact-hooksrendering

why child components div in react is not re rendering even though child component re renders at state change


post clicked on complete buttonenter image description hereI have 2 componets app.js and layput.js . In app.js I have todo state which will have JSON Object which gets added when Add button from app.js is clicked. In layout.js using .map function have printed the object passed as prop from app.js. there are 2 buttons in layout.js which are deleting and updating json value where delete button works properly and re renders child component but update button is not working even though layout.js is getting re renders

app.js
import './App.css';
import Layout from './pages/layout';
import React, { useRef, useState } from 'react';
function App() {
  const [todo, setTodo] = useState([]);
  const titleRef = useRef();
  const todoRef = useRef();
  let refid=useRef(0);
  console.log("render in app",refid);
  function Addtodo() {
    setTodo([...todo, { id:refid.current++,title: titleRef.current.value, todo: todoRef.current.value, isComplete: false }]);
  }
  function deleteTodo(title) {
    setTodo(todo.filter(filterItem => filterItem.title !== title));
  }
  function updateTodo(title) {
    let newtodo=todo;
    for (let i=0;i<todo.length;i++ )
    {
      if (todo[i].title === title && !todo[i].isComplete) {        
        todo[i].isComplete = true;
        todo[i].todo=todo[i].todo+'1';
      }
    }    
      setTodo(todo);      
  }
  return (
    <div className="App">
      Title
      <input ref={titleRef} id="title" type="text" />
      To Do
      <input ref={todoRef} id="todo" type="text" />
      <button type="button" onClick={Addtodo}>Add</button>
      <Layout todoArray={todo} deleteTodo={deleteTodo} updateTodo={updateTodo} />
    </div>
  );
}

export default App;

layout.js
import React, { useContext, useEffect } from 'react';
function Layout(props) {
    const ondelete = (e) => {
        props.deleteTodo(e.target.name);
    }
    const onupdate = (e) => {        
        props.updateTodo(e.target.name);
    }
console.log('props',props.todoArray);
    return (<>
        <h1>Hello</h1>
        {props.todoArray.map((todos) => {
            return <div key={todos.id}>
                <span><b> Title :</b> {todos.title}</span>
                <span><b> To Do :</b> {todos.todo}</span>
                <span><b> Is Complete :</b> {todos.isComplete.toString()}</span>
                <button name={todos.title} onClick={onupdate}>Complete</button>
                <button name={todos.title} onClick={ondelete}>Delete</button>
            </div>
        })}
    </>);
}

export default Layout;

i want print tag Is Complete to True if we clicked on Complete Button


Solution

  • React uses shallow comparison for prop changes, this means that if the todoArray prop is passed the same object reference, it will not re-render, even if the array elements have changes.

    The solution is to call setTodo with a new array every time.

    We can ensure that a new array is created, instead of modifying the old array, by spreading the old array into your newTodo var.

      function updateTodo(title) {
        let newtodo = [...todo];
        for (let i = 0; i < todo.length; i++) {
          if (todo[i].title === title && !todo[i].isComplete) {
            todo[i].isComplete = true;
            todo[i].todo = todo[i].todo + '1';
          }
        } setTodo(todo);
      }