Minsaf
Minsaf

Reputation: 274

Table values does not change in react

Im fetching data from an API and changing the value from the frontend to display it in a table. Im fetching a list of objects and storing it in a state and displaying the objects in the state in a html table. The table has a checkbox to display a boolean value. If the value is true, then defaultChecked is true in the checkbox. There's a checkbox in the table header to check or uncheck all items.

The following is the json object which i fetch.

{
completed: false
id: 1
title: "delectus aut autem"
userId: 1
}

If I checked the checkbox in the table header, I want to set completed to true in all 200 items.

The following is the code to set all items to either true or false.

const checkAllHandler = (e) => {
    let val = e.target.checked;
    console.log(val);
    let allTodoList = [];

    if (val === true) {
      if (todo.length > 0) {
        
        for (let index = 0; index < todo.length; index++) {
          const newObject = {
            userId: todo[index].userId,
            id: todo[index].id,
            title: todo[index].title,
            completed: true,
          };
          allTodoList.push(newObject);
        }
        setTodo(allTodoList);
        
      }
    } else if (val === false) {
      if (todo.length > 0) {
       
        for (let index = 0; index < todo.length; index++) {
          const newObject = {
            userId: todo[index].userId,
            id: todo[index].id,
            title: todo[index].title,
            completed: false,
          };
          allTodoList.push(newObject);
        }
        setTodo(allTodoList);
        
      }
    }
  };

When I console the todo state, all the values are updated to either true or false but it doesnt show on the table. The table has a filter function. If I filter a word and go back, then the values of entire table is changing. I want to display the change when the checkbox is clicked and not when search and go back. How to I do that?

The complete code of the component is below.

const DataTable = () => {
  const { loading, items } = useSelector((state) => state.allData);
  // console.log(items)

  const dispatch = useDispatch();
  // const history = useNavigate();

  const [searchText, setsearchText] = useState("");
  const [todo, setTodo] = useState(items);
  console.log("todo");
  console.log(todo);
  const [isFetch, setisFetch] = useState(false);
  const [checkedloading, setcheckedLoading] = useState(false);
  const [isChecked, setisChecked] = useState(false);
  console.log(isChecked);

  useEffect(() => {
    const setDataToState = () => {
      if (loading === false) {
        setTodo(items);
      }
    };
    setDataToState();
  }, [loading]);

  useEffect(() => {
    
    dispatch(getData());
    // setTodo(items)
    // console.log(items)
  }, [dispatch]);
  //etTodo(items)
  const handleSearch = (event) => {
    //let value = event.target.value.toLowerCase();
    setsearchText(event.target.value);
  };

  const onChangeHandler = (e, item) => {
    console.log(e.target.checked);
    console.log(item);

    if (e.target.checked === true) {
      // const item = todo.filter(x=> x.id === id)
      // console.log("added")
      // console.log(item)

      for (let index = 0; index < todo.length; index++) {
        if (todo[index].id === item.id) {
          console.log(todo[index]);
          console.log("deleting");
          todo.splice(index, 1);
          console.log("deleted");
          const newObject = {
            userId: item.userId,
            id: item.id,
            title: item.title,
            completed: true,
          };
          console.log("adding updated object");
          todo.splice(index, 0, newObject);
          console.log("added");

          console.log(todo);
        }
      }
    } else {
      // const item = todo.filter(x=> x.id === id)
      // console.log("removed")
      // console.log(item)
      for (let index = 0; index < todo.length; index++) {
        if (todo[index].id === item.id) {
          console.log(todo[index]);
          console.log("deleting");
          todo.splice(index, 1);
          console.log("deleted");
          const newObject = {
            userId: item.userId,
            id: item.id,
            title: item.title,
            completed: false,
          };
          console.log("adding updated object");
          todo.splice(index, 0, newObject);
          console.log("added");

          console.log(todo);
        }
      }
    }
  };

  const onSubmitHandler = () => {
    localStorage.setItem("items", JSON.stringify(todo));
  };
  const getItem = () => {
    const items = localStorage.getItem("items");
    console.log("local storage items");
    console.log(JSON.parse(items));
  };

  const checkAllHandler = async (e) => {
    const { checked } = e.target;
    console.log(checked);
    setTodo((todos) =>
      todos.map((todo) => ({
        ...todo,
        completed: checked,
      }))
    );
   
  };

  return (
    <>
      {console.log("todo in render")}
      {console.log(todo)}
      <div className={styles.container}>
        <div className={styles.top}>
          <div className={styles.search_bar}>
            <input
              type="text"
              onChange={(e) => handleSearch(e)}
              placeholder="search by name"
            />
          </div>
        </div>

        <div className={styles.btn_container}>
          <button onClick={onSubmitHandler}>Submit</button>
        </div>

        <div className={styles.data_table_container}>
          {checkedloading === false ? (
            <>
              <div className={styles.data_table}>
                {loading || todo === null || todo === undefined ? (
                  <>
                    <p>Loading!!</p>
                  </>
                ) : (
                  <>
                    <table>
                      <tr>
                        <th>ID</th>
                        <th>userId</th>
                        <th>Title</th>
                        <th>
                          <>
                            Completed
                            <input type="checkbox" onChange={checkAllHandler} />
                          </>
                        </th>
                      </tr>
                      <tbody>
                        {todo
                          .filter((val) => {
                            if (searchText === "") {
                              return val;
                            } else if (
                              val.title.toLowerCase().includes(searchText)
                            ) {
                              return val;
                            }
                          })
                          .map((item) => (
                            <>
                              <tr key={item.id}>
                                <td>{item.id}</td>
                                <td>{item.userId}</td>
                                <td>{item.title}</td>
                                <td>
                                  <input
                                    type="checkbox"
                                    defaultChecked={item.completed}
                                    onClick={(e) => onChangeHandler(e, item)}
                                  />
                               
                                </td>
                               
                              </tr>
                            </>
                          ))}
                      </tbody>
                    </table>
                  </>
                )}
              </div>
            </>
          ) : (
            <>Loading</>
          )}
        </div>
      </div>
    </>
  );
};

CodeSandbox link: https://codesandbox.io/s/flamboyant-proskuriakova-60t19

Upvotes: 1

Views: 829

Answers (1)

Drew Reese
Drew Reese

Reputation: 202686

I don't see any overt issues in this code, but it is quite verbose. Both logic branches are identical save for the completed boolean value assigned. When updating arrays in React it is common to use functional state updates to make the shallow copy of the previous state, not whatever state may be closed over in the callback scope.

Example:

const checkAllHandler = (e) => {
  const { checked } = e.target;
  console.log(checked);
  setTodo(todos => todos.map(todo => ({
    ...todo,
    completed: checked,
  })));
};

If there's still further issue from here with updating the table then please add the rest of the component code and any other code you think is relevant.

Update

The reason none of the checkbox inputs are changing in the table is because you've used the defaultChecked prop, which makes these inputs fully uncontrolled inputs. They take the initial item.completed value when mounted and don't change from there other than if you interact with the checkbox.

If you want them to respond to changes/updates to the todo state then they should be converted to fully controlled inputs and use the checked prop.

<input
  type="checkbox"
  checked={item.completed}
  onClick={(e) => onChangeHandler(e, item)}
/>

Edit dazzling-sunset-t5z80

Update #2

The individual checkbox inputs were being mutated with Array.prototype.splice in onChangeHandler. .splice does an in-place mutation. Again a functional state update should be used to shallow copy the previous state and check for the matched todo object by id.

const onChangeHandler = (e, item) => {
  const { checked } = e.target;

  setTodo((todos) =>
    todos.map((todo) =>
      todo.id === item.id
        ? {
            ...todo,
            completed: checked
          }
        : todo
    )
  );
};

Upvotes: 1

Related Questions