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>
}
CodePudding user response:
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} />)
<.../>
}
}