Skip to content
Advertisement

How do I keep the state of a React Component after removing others from an array?

I’m new to React and am not sure what I’m doing wrong here. I have a component called Blocks that contains an array of sub-components in state. Right now, when I add the sub-component Paragraph, I do so like this. This is in the parent component Blocks.

handleAddBlock(block) {

    let new_block = null;

    let last_block_id = this.state.last_block_id;
    last_block_id++;

    new_block = {
        component: <Paragraph
            key={last_block_id}
            id={last_block_id}
        />,
        id: last_block_id,
        value: null
    }


    this.setState({ last_block_id: last_block_id });
    this.setState({ blocks: [...this.state.blocks, new_block] });
}

The Paragraph component has a state variable “value”, that is updated when a user types into a text box. However, when I go to remove an item from this.state.blocks, any components that come after the component I’m removing all get re-rendered, and lose their state. The components that come before the item I’ve removed keep theirs.The question is why, and how can I stop that from happening? Is this a bad design pattern?

Here’s the code that handles the removal of a sub-component. This is in the parent component Blocks.

handleRemoveBlock(id) {

    const blocks = [...this.state.blocks].filter(block => {
        return block.id !== id;
    });

    this.setState({ blocks: blocks });
}

And finally, this is part of the render() method in the parent component Blocks.

render() {

    const blocks = this.state.blocks.map(block => {
        return <div
            key={block.key}
            className="col p-1"
        >{block.component}

            <button
                className="delete-button"
                onClick={() => this.handleRemoveBlock(block.id)}
                type="button">X
            </button>
        </div>
    })

    return <section className="row">

        <div className="col">

            <div className="col">
                {blocks}
            </div>
        </div>
    </section>
}

Advertisement

Answer

I have a component called Blocks that contains an array of sub-components in state.

You shouldn’t. Components should contain as little data in their state as possible. The main React design concept is that component’s render method is a pure function of props and the state. Based on this, you should move <Paragraph/> instances (because you should render components only in render) and last_block_id (because it’s computable from the blocks state) from state to render:

class Block extends React.Component {
  handleAddBlock(block) {
    const new_block = { ... } 
    this.setState('blocks', [...this.state.blocks, new_block])
  }

  get last_block_id() {
    return this.state.blocks.at(-1).id
  }

  render() {
    // your markup
    return <...>
      // create Paragraph here
      {this.state.blocks.map(block => <Paragraph key={block.id} id={block.id} />)
    <.../>
  }
}
User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement