I am trying to let users remove values from there current settings.
I have a series of values users can have for there settings that are displayed in a pill format that they can click X on to delete, i.e.,
export default function Pill({ value, handleChange }) {
  // const [times, setTimes] = useState([]);
    return (
      <div className="bg-gray-600 text-white text-xs px-2 py-0.5 w-max-content rounded-lg align-middle mr-1 mb-1">
        {value}
        <svg
          xmlns="http://www.w3.org/2000/svg"
          width="16"
          height="16"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          strokeWidth="2"
          strokeLinecap="round"
          strokeLinejoin="round"
          className="inline-block align-middle cursor-pointer"
          onClick={() => handleChange(value, "remove")}
        >
          <line x1="18" y1="6" x2="6" y2="18" />
          <line x1="6" y1="6" x2="18" y2="18" />
        </svg>
      </div>
    );
  }
This is used as a component as below,
{substanceDetails &&
              substanceDetails.map((subst) => (
                <Pill
                  registerInput={register}
                  optionLabel="substance"
                  value={subst.substance}
                  key={subst.substance}
                  optionName="substance"
                  allOptions={["Dexamphetamine", "Ritalin"]}
                  handleChange={handleChange}
                />
              ))}
Given the handle change, which needs to be changed, but it would look something like this,
const handleChange = (value, mode) => {
    let updatedValues = values;
    if (mode === 'remove') {
      updatedValues = values.filter((elem) => elem !== value);
    } else if (!updatedValues.includes(value)) updatedValues = [...values, value];
    setValues(updatedvalues);
  };
                You are mutating state in your handleChange function:
const handleChange = (value, mode) => {
    let updatedValues = values; //takes the reference to state variable
    if (mode === 'remove') {
      updatedValues = values.filter((elem) => elem !== value); //mutation
    } else if (!updatedValues.includes(value)) updatedValues = [...values, value];
    setValues(updatedvalues);
  };
You can use something like this:
const handleChange = (value, mode) => {
    if(mode==="remove") setValues(vals => vals.includes(value) ? vals.filter(v => v !== value) : [...vals, value])
  };
.includes() won't work as expected if your array elements are not primitive values. You can use .find() or .some() in that case:
Example of filtering by the field duration for simplicity:
const handleChange = (value, mode) => {
    if(mode==="remove") setValues(vals => vals.find(v=> v.duration === value.duration) ? vals.filter(v => v.duration !== value.duration) : [...vals, value])
  };
                        After reading your code, I found the main problem might not be the mutation issue, but you didn't manage the state very well.
First, your substanceDetails should be stored in YourSubstanceListComponent component, not in your Pill component, something like this. This is the main reason why your first approach does not work.
// in YourSubstanceListComponent
const YourSubstanceListComponent = () = {
  const [values, setValues] = useState([]);
  const handleChange = (value, mode,optionName) => {
    // Should call `setValues` in this function
    const newValues = [];// TODO
    setValues(newValues);
  }
  return (
    <>
      {values.map(v => <Pill {...otherPillProps} value={v.substance} />)}
    </>
  )
}
Then, your handleChange should be placed in YourSubstanceListComponent. The reason why is you might don't want to pass setValues to your Pill component.
Next is the part about getting new removed values items. According the docs:
They don't modify the original array. So you can use them like in your code and it should work.
But, I found some more problems in your code and I think you should check it again.
map (when the number of elements does not change) or filter (when the number of elements will change). I rarely use any other methods apart from map and filter in all my life.handleChange function, I don't see the optionName variable. Where does it come from? Maybe you didn't update your Pill component.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With