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