0

I have a table of text boxes and want a given text box to highlight when I change it. I have the CSS class but am not able to apply it to a specific field. Currently the setChanged() makes the entire table highlighted. I should be able to focus it to a given field given that I have access to the ID and the field name within the event handler righ?

function AppCookie() {

    const [cookies, setCookies] = useState();
    const [changed, setChanged] = useState('');

    const onChangeInput = (e, id) => {
        const { name, value } = e.target
       
        setChanged('highlighted-input')
        const editData = cookies.map((cookie) =>
            cookie.id === id && name ? { ...cookie, [name]: value } : cookie
        )
        editData[id-1].name
        setCookies(editData)
    }

        const handleClick = async (event) => {
       
          ...}

      useEffect(() => {
          populateCookieData();
       }, []);

    const contents = cookies === undefined
        ? <p><em>Loading...</em></p> 
       
        : 
        <table className="table table-striped" aria-labelledby="tabelLabel">
            <thead>
                <tr>
                    <th>Id</th>
                    <th>Date</th>
                    <th>Name</th>
                    <th>Desc</th>
                    <th>Price</th>
                </tr>
            </thead>
            <tbody>
                {cookies.map(cookie =>
                    <tr key={cookie.id}>
                        <td>{cookie.id}</td>
                        <td>{cookie.date}</td>
                        <td>
                         <input
                            name="name"
                            value={cookie.name}
                            type="text"
                            onChange={(e) => onChangeInput(e, 
      cookie.id)}
                            placeholder="Type Name"
                            className={changed} />
                        </td>
                       
                        <td><input
                            name="desc"
                            value={cookie.desc}
                            type="text"
                            onChange={(e) => onChangeInput(e, cookie.id)}
                            placeholder="Type Desc"
                        /></td>
                         <td><input
                            name="price"
                            value={cookie.price}
                            type="text"
                            onChange={(e) => onChangeInput(e, cookie.id)}
                            placeholder="Type Price"
                            className={changed}/></td>
                        <td>  <button type="submit" onClick={() => handleClick({cookie})}>Update</button></td>
                        
                    </tr>
                )}
            </tbody>
        </table>;



  
    return (
        <div>
            <h1 id="tabelLabel">Cookies</h1>
            {contents}
        </div>
    );

    async function populateCookieData() {
     ...
    }
}

1 Answer 1

1

I do not see all your code, but do you use the useState hook:

const [changed, setChanged] = useState('');

in the same component where you do cookies.map?

The state created by useState hook is used for the whole component where you call this hook. You store just "highlighted-input" string there, and the changed variable is visible and is absolutely the same for every cookie in your cookies.map cycle.

In order to highlight a specific input, you must save which one to highlight. And at the moment of the loop where you display the inputs, check if the current one is highlighted.

I have prepared a code sample that highlights the last edited input. For this purpose, when changing an input in the onChangeInput handler, I use setState to store the ID of the changed cookie.

const cookies = [
  { id: "lang", desc: "en" },
  { id: "theme", desc: "dark" },
];

const highlighted = "highlighted-input";

function CookiesTable() {
  // create the initial state with empty string
  const [lastChangedId, setLastChangedId] = React.useState("");

  const onChangeInput = (e, id) => {
    const { name, value } = e.target;
    // write the id of changed cookie to the lastChangedId state
    setLastChangedId(id);
  };

  return (
    <table>
      {cookies.map((cookie) => (
        <tr key={cookie.id}>
          <td>{cookie.id}</td>
          <td>
            <input
              name="desc"
              defaultValue={cookie.desc}
              type="text"
              onChange={(e) => onChangeInput(e, cookie.id)}
              placeholder="Type Desc"
              className={lastChangedId === cookie.id ? highlighted : ""}
            />
          </td>
        </tr>
      ))}
    </table>
  );
}

const rootElement = document.getElementById("root");
const root = ReactDOM.createRoot(rootElement);

root.render(<CookiesTable />);
.highlighted-input {
  background-color: aqua;
}
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>

If you want, for example, to highlight not only the last but all changed inputs, it is better to store an object in the state:

// Create the initial state with empty object
const [changed, setChanged] = useState({});

const onChangeInput = (e, id) => {
  const { name, value } = e.target;
  // add a new property with current cookie id when input is chagned
  setChanged((allChanged) => ({
    ...allChanged,
    [id]: true,
  }));
};

// ... all other code ...

// check, if current cookie.id is presented in the "changed" state
className={changed[cookie.id] ? "highlighted-input" : ""}
6
  • Thanks! I updated my question to include more of the code to clear up the context. As it stands I can't declare another cookies object like you have...
    – John
    Commented Jun 13 at 19:49
  • It doesn't matter exactly how you get the cookies array. Note how className is set on input in my example, and how lastChangedId/setLastChangedId is set in the snippet or changed/setChanged in my second code example.
    – isqua
    Commented Jun 13 at 19:56
  • Is this what the ternary would like with multiple fields being checked? {(changed[cookie.id] && changed[cookie.name]) ? "highlighted-input" : ""}
    – John
    Commented Jun 13 at 20:34
  • If you want to highlight every single input independently, you need to store not only cookie.id, but also what field was changed. Have a look at this example: codesandbox.io/p/sandbox/… In the state I store an object with "name", "desc" and "price" properties. In the onChangeInput handler I the changed[column][cookie.id] value to true. And when I calculate classNames for the input, I check it like changed.desc[cookie.id]
    – isqua
    Commented Jun 14 at 5:58
  • OK it works! btw if I wanted to reverse the highlight on an Update, do I just set the ID to false?
    – John
    Commented Jun 14 at 15:06

Not the answer you're looking for? Browse other questions tagged or ask your own question.