Reputation: 755
Please reference this Codesandbox for my example.
I am having problems deleting components that get rendered via a function. Since components should not be stored in state, I store their type in an array in the parent component's state instead. A function iterates through that array to render the components. When I attempt to delete a specific component by index, React does not update the props (the index) in the component I want to keep (ie: changing the component at index 2 down to index 1). Instead, it renders the component already at that index. My Codesandbox example demonstrates this behavior.
How can I delete a component that is rendered in this way (via a function that uses the parent's state to populate props)? Is there another way I could be rendering these components?
Upvotes: 1
Views: 3513
Reputation: 4266
It is kind of hard for me to make sense of your code but the live demo looks clear enough.
Your state is an array and each item can be rendered as an isolated component. What you want to do is let the state flow down into your components, and wire them up to update it properly (when you click the delete key, an array item needs to be removed).
Here's how you can achieve this in a much simpler way: CodeSandbox demo.
Upvotes: 1
Reputation: 2723
Like @MichaelRovinsky said:
Give each component a unique identifier. When you delete a component, find it by id and remove from the array. Don't use index because it's not persistent
So, you need to use a unique identifier.
For that, you can use react-uuid.
First, I've redefined your step as:
export class Step {
Id;
Type;
Value;
}
So, your default data should be like:
const defaultSteps = [
{ Type: ALPHA, Id: uuid(), Value: 0 },
{ Type: ALPHA, Id: uuid(), Value: 0 },
{ Type: ALPHA, Id: uuid(), Value: 0 }
];
I've also changed Alpha
component's index
prop with id
.
Inside your renderSteps
function:
case ALPHA:
return (
<Alpha
id={step.Id}
onChange={onValueChange}
onDelete={index > 0 ? () => onDelete(step) : null}
key={step.Id}
/>
);
See, now both the id
prop and key
are the same and unique.
onValueChange
event:
const onValueChange = (id, newValue) => {
let currentSteps = [...steps];
const step = currentSteps.filter((f) => f.Id === id)[0];
step.Value = newValue;
setSteps(currentSteps);
setValues(currentSteps.map((m) => m.Value));
};
So, setting your values becomes much simpler without tracing indexes.
onDelete
event:
const onDelete = (step) => {
let currentSteps = [...steps];
const index = currentSteps.indexOf(step);
currentSteps.splice(index, 1);
setSteps(currentSteps);
setValues(currentSteps.map((m) => m.Value));
};
As all of your required properties are now inside single object, it's much easier to handle both steps
and values
state.
Working demo at CodeSandbox.
Upvotes: 2