L Becker
L Becker

Reputation: 755

How to remove a component React has rendered via a function

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

Answers (2)

Janez Kuhar
Janez Kuhar

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

Zunayed Shahriar
Zunayed Shahriar

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

Related Questions