How to use React useRef hook for removing classes within map

Viewed 3690

I'm new to React. I'm attempting to add an onClick event to a div element that will remove a className from another element. These elements are part of a loop in a map. I am attempting to use the useRef hook for this. I specifically don't want to toggle classNames, I want to remove it, and that's it. Then add it back with another onclick event from another element. This requirement is specific to my application. My current code removes the className from the last element, not the one I am targeting. Thanks in advance for any help!

import React, { useState, useEffect, useRef } from "react";
import axios from "axios";
import "./styles.css";

export default function App() {
  const [kitchenItems, setkitchenItems] = useState([]);

  useEffect(() => {
    axios.get("./data.json").then((res) => {
      setkitchenItems(res.data.kitchen);
    });
  }, []);

  const navRef = React.useRef(null);

  const onRemoveClick = (e) => {
    navRef.current.classList.remove("red");
  };

  return (
    <main>
      {kitchenItems.map((item, index) => (
        <div key={index} className="item">
          <div onClick={onRemoveClick}>
            <h2>{item.name}</h2>
            <p ref={navRef} className="red">
              {item.text}
            </p>
          </div>
        </div>
      ))}
    </main>
  );
}

Here it is in CodeSandbox: https://codesandbox.io/s/lively-moon-d7mm3

3 Answers

Save the indicator of whether an item should have the class or not into the kitchenItems state. Remove the ref.

  useEffect(() => {
    axios.get("./data.json").then((res) => {
      setkitchenItems(res.data.kitchen.map(item => ({ ...item, red: true })));
    });
  }, []);
const onRemoveClick = (i) => {
  setkitchenItems(
    kitchenItems.map((item, j) => j !== i ? item : ({ ...item, red: false }))
  );
};
<div onClick={() => onRemoveClick(i)}>
  <h2>{item.name}</h2>
  <p className={item.red ? 'red' : ''}>

The way you are approaching this is the way it's done with jQuery, that you change the dom elements. In React you would instead update the state and let the component render. I have added a new property on your objects but you may have another list or some other logic if you wish.

import React, { useState, useEffect, useRef } from "react";
import axios from "axios";
import "./styles.css";

export default function App() {
  const [kitchenItems, setkitchenItems] = useState([]);

  useEffect(() => {
    axios.get("./data.json").then((res) => {
      setkitchenItems(res.data.kitchen);
    });
  }, []);

  const onRemoveClick = (index) => {
    debugger;
    const tmpItems = [...kitchenItems];
    tmpItems[index].isRed = !tmpItems[index].isRed;
    setkitchenItems(tmpItems);
  };

  return (
    <main>
      {kitchenItems.map((item, index) => (
        <div key={index} className="item">
          <div onClick={() => onRemoveClick(index)}>
            <h2>{item.name}</h2>
            <p className={item.isRed ? "red" : ""}>{item.text}</p>
          </div>
        </div>
      ))}
    </main>
  );
}

In CodeSandbox: https://codesandbox.io/s/keen-kirch-55whe?file=/src/App.js

Why not just use pureJS?

const onRemoveClick = (e) => {
    var target = e.target;
    if (target) {
      if (target.classList && target.classList.contains("red")) {
        target.classList.remove("red");
      } else {
        target.classList.add("red");
      }
    }
  };
Related