Search code examples
wordpressgutenberg-blocks

WordPress block update resets attribute array


I working on a WordPress Gutenberg block that has the following attributes:

attributes: {
    panels: {
        type: "array",
        default: [{
            id: uuid(),
            h2: "",
            h3: "",
            backgroundImage: "",
            highlightText: "",
            featureList: [""],
            buttonText: "",
            buttonUrl: "",
            buttonColor: ""
        }
    }
}

As you can see, it is an array of objects (panels[]). The problem I'm having is this: when there are 2 or more panels and I start typing into one of the panels (the h2, for example) all other panels disappear.

Here is the JSX that is returned:

return (
        <div {...blockProps}>
            <h2 className="tpp-heading">Tariff Product Panel</h2>
            {props.attributes.panels.map((panel, panelIndex) => {
                return (
                    <div className="tpp-product" id={panel.id}>
                        <h2 className="tpp-sub-heading">Product {panelIndex + 1}</h2>
                        <Button
                            onClick={() => removePanel(panelIndex)}
                            className="tpp-remove-panel"
                            title="Delete Panel">
                            <Icon
                                icon={
                                    "remove"
                                }
                            />
                        </Button>
                        <Flex>
                            <FlexBlock className="tpp-attribute">
                                <TextControl
                                    value={panel.h2}
                                    label="Heading"
                                    onChange={(value) => updateAttribute(panelIndex, value, "h2", panel.id)}
                                />
                            </FlexBlock>
                            <FlexBlock>
                                <TextControl
                                    value={panel.h3}
                                    label="Sub Heading"
                                    onChange={(value) => updateAttribute(panelIndex, value, "h3", panel.id)}
                                />
                            </FlexBlock>
                        </Flex>
                        <Flex>
                            <FlexBlock className="tpp-attribute">
                                <TextControl
                                    value={panel.backgroundImage}
                                    label="Background Image"
                                    onChange={(value) => updateAttribute(panelIndex, value, "backgroundImage", panel.id)}
                                />
                            </FlexBlock>
                            <FlexBlock>
                                <TextControl
                                    value={panel.highlightText}
                                    label="Highlight Text (if filled in, panel will have highlight)"
                                    onChange={(value) => updateAttribute(panelIndex, value, "highlightText", panel.id)}
                                />
                            </FlexBlock>
                        </Flex>
                        <Flex>
                            <FlexBlock className="tpp-attribute">
                                <TextControl
                                    value={panel.buttonText}
                                    label="Button Text"
                                    onChange={(value) => updateAttribute(panelIndex, value, "buttonText", panel.id)}
                                />
                            </FlexBlock>
                            <FlexBlock>
                                <SelectControl
                                    label="Button Color"
                                    value={panel.buttonColor}
                                    options={[
                                        { label: 'Default', value: 'default' },
                                        { label: 'Primary', value: 'primary' },
                                        { label: 'Secondary', value: 'secondary' },
                                    ]}
                                    onChange={(value) => updateAttribute(panelIndex, value, "buttonColor", panel.id)}
                                />
                            </FlexBlock>
                        </Flex>
                        <Flex>
                            <FlexBlock className="tpp-attribute">
                                <TextControl
                                    value={panel.buttonUrl}
                                    label="Button URL"
                                    onChange={(value) => updateAttribute(panelIndex, value, "buttonUrl", panel.id)}
                                />
                            </FlexBlock>
                        </Flex>
                        <label class="components-base-control__label css-1v57ksj">Features:</label>
                        {panel.featureList.map((feature, featureIndex) => {
                            return (
                                <Flex>
                                    <FlexBlock>
                                        <TextControl
                                            value={feature}
                                            onChange={(feature) => updateFeaturelist(panelIndex, featureIndex, feature)}
                                        />
                                    </FlexBlock>
                                    <FlexBlock>
                                        <Button
                                            onClick={() => removeFeature(panelIndex, featureIndex)}
                                            className="tpp-delete-feature"
                                            title="Delete Feature">
                                            <Icon
                                                icon={
                                                    "trash"
                                                }
                                            />
                                        </Button>
                                    </FlexBlock>
                                </Flex>
                            )
                        })}
                        <Button
                            onClick={() => addNewFeature(panelIndex)}
                            className="tpp-add-feature">
                            Add feature
                        </Button>
                    </div>
                )
            })}
            <Button
                isPrimary
                onClick={() => addNewProduct()}>
                Add product
            </Button>
        </div>
    )
}

And here is the function that updates the panel:

function updateAttribute(panelIndex, value, el, id) {

        let panel = props.attributes.panels.find((item) => item.id == id)

        panel[el] = value

        props.setAttributes({
            panels: props.attributes.panels.splice(panelIndex, 1, panel)
        })
    }

I'd really appreciate a bit of help with this. Everything else is working fine.

In the updateAttribute function I've tried various ways to get this to work but without any luck. Obviously, what I'm expecting is that when typing into a panel, the other panels do not disappear.


Solution

  • OK... I've managed to figure it out - but it took quite a while!

    So it turns out that I needed to update the a copy of the whole array (panels) first before calling setAttribute.

    function updateAttribute(panelIndex, value, el, id) {
    
            let panel = props.attributes.panels.find((item) => item.id == id)
    
            panel[el] = value
    
            const newPanels = [...props.attributes.panels]
    
            newPanels[panelIndex] = panel;
    
            props.setAttributes({
                panels: newPanels
            })
        }

    I'm still a bit confused as to why my earlier attempts were not working but at least I've managed to solve my issue.