skip to Main Content

I’m trying to build a basic app in react. I have a checkbox for each item in the table and once if it is checked, I want to delete that item. But for now, I was just checking the working of checkbox. But my onChange is not triggering the handler function.

I have added a console log to my handleOnChange function, I can see it’s not getting logged which means my ‘onChange‘ is not triggering the handler.

Here is my Table.js code:

import React, { useState, useEffect } from "react";
import axios from "axios";


function Table() {
    const [items, setItems] = useState([]);
    const [isChecked, setIsChecked] = useState([false]);

    useEffect(() => {
        const loadData = async () => {

            await axios.get('http://localhost:9000/')
                .then(function (res) {
                    setItems(res.data);
                })
                .catch(function (err) {
                    console.log(err);
                });
        }

        loadData();
    }, [items]);

    const handleOnChange = (index,e) => {
        e.preventDefault();
        console.log("Change event triggered");
        setIsChecked(prevState => {
            const newState = [...prevState];
            newState[index] = !newState[index];
            return newState;
        });

    };



    const ItemList = ({ items }) => {
        const listItems = items.map((item) => (
            <tr className="border border-slate-300">
                <td>
                    <input type="checkbox" name="checkbox" checked={isChecked[item._id]} onChange={(e) => handleOnChange(item._id,e)} />
                </td>
                <td key={item.name}>
                    {item.name}
                </td>
                <td key={item.quantity}>
                    {item.quantity}
                </td>
                <td key={item.price}>
                    {item.price}
                </td>
            </tr>
        ));

        return (
            <>
                {listItems}
            </>
        );

    };




    return (
        <div className="grid grid-flow-col auto-rows-max max-w-full justify-center place-content-center">
            <table className="table-auto">
                <thead>
                    <tr>
                        <th className="border border-slate-300 ...">Checked</th>
                        <th className="border border-slate-300 ...">Items</th>
                        <th className="border border-slate-300 ...">Quantity</th>
                        <th className="border border-slate-300 ...">Price</th>
                    </tr>
                </thead>
                <tbody>
                    <ItemList items={items} />
                </tbody>
            </table>
        </div>
    );
}

export default Table;

2

Answers


  1. the issue might be the isChecked and how you are managing the checked prop of your input. The isChecked array is initialized as an empty array with a single false value and you’re accessing isChecked[item._id] to determine the checked status.
    Since the isChecked is initially [false] and this will not change in your code, isChecked[item._id] will be undefined.

    You should setIsChecked when you’re fetching the data:

      // ...
      const [isChecked, setIsChecked] = useState([]);
    
      useEffect(() => {
        const loadData = async () => {
          try {
            const res = await axios.get("http://localhost:9000/");
            setItems(res.data);
            setIsChecked(Array(res.data.length).fill(false));
          } catch (err) {
            console.log(err);
          }
        };
    
        loadData();
      }, []);
    
      //...
    
    Login or Signup to reply.
  2. Do not call e.preventDefault() inside the onChange handler. It makes you have to press the input twice to toggle on/off.

    Also, try to make the row its own component.

    const { useCallback, useEffect, useState } = React;
    
    const fetchData = () => Promise.resolve([
      { _id: 1, name: 'Foo', quantity: 1, price: 5.99 },
      { _id: 2, name: 'Bar', quantity: 2, price: 9.99 }
    ]);
    
    const ItemRow = ({ _id, checked, handleOnChange, name, price, quantity }) => (
      <tr key={name} className="border border-slate-300">
        <td className="text-center">
          <input
            type="checkbox"
            name="checkbox"
            checked={checked}
            onChange={(e) => handleOnChange(_id, e)} />
        </td>
        <td>{name}</td>
        <td className="text-right">{quantity}</td>
        <td className="text-right">{price}</td>
      </tr>
    )
    
    const ItemList = ({ handleOnChange, isChecked, items }) => (
      <React.Fragment>
      {
        items.map((item, index) => (
          <ItemRow {...item}
            key={item.name}
            checked={isChecked[index]}
            handleOnChange={handleOnChange} />
        ))
      }
      </React.Fragment>
    );
    
    const Table = () => {
      const [items, setItems] = useState([]);
      const [isChecked, setIsChecked] = useState([]);
      
      useEffect(() => {
        fetchData()
          .then(data => {
            setItems(data);
            setIsChecked(Array.from(data, () => false));
          })
      }, []);
    
      const handleOnChange = useCallback((id, e) => {
        setIsChecked((prevState) => {
          const newState = [...prevState];
          const index = items.findIndex(item => item._id === id);
          newState[index] = !newState[index];
          return newState;
        });
      }, [items]);
    
      return (
        <div className="grid grid-flow-col auto-rows-max max-w-full justify-center place-content-center">
          <table className="table-auto">
            <thead>
              <tr>
                <th className="border border-slate-300 p-2">Checked</th>
                <th className="border border-slate-300 p-2">Items</th>
                <th className="border border-slate-300 p-2">Quantity</th>
                <th className="border border-slate-300 p-2">Price</th>
              </tr>
            </thead>
            <tbody>
              <ItemList
                handleOnChange={handleOnChange}
                isChecked={isChecked}
                items={items} />
            </tbody>
          </table>
        </div>
      );
    };
    
    ReactDOM.createRoot(document.getElementById("root")).render(<Table />);
    <link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet"/>
    <div id="root"></div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search