Consider the following blocks.
block.js
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"name": "create-block/demo-block",
"version": "0.1.0",
"title": "Demo Block",
"category": "widgets",
"icon": "smiley",
"description": "Example block scaffolded with Create Block tool.",
"attributes": {
"arr": {
"type": "array",
"default": []
}
},
"supports": {
"html": false
},
"textdomain": "demo-block",
"editorScript": "file:./index.js"
}
edit.js
import {
useBlockProps,
InspectorControls
} from '@wordpress/block-editor';
import {
Panel,
PanelBody
} from '@wordpress/components';
import './editor.scss';
import { DemoObject } from './demo';
export default function Edit({ attributes, setAttributes }) {
return (
<>
<InspectorControls>
<Panel>
<PanelBody title='Demo Setting'>
<DemoObject
arr = { attributes.arr }
update = { (newArr) => setAttributes({ arr: newArr }) }
/>
</PanelBody>
</Panel>
</InspectorControls>
<p { ...useBlockProps() }>
{ 'Demo Block – hello from the editor!' }
</p>
</>
);
}
demo.js
import { Component } from '@wordpress/element';
import { Button } from '@wordpress/components';
export class DemoObject extends Component {
constructor(props) {
super(props);
this.state = {
arr: props.arr,
updater: props.update
}
this.increase = this.increase.bind(this);
this.decrease = this.decrease.bind(this);
}
increase() {
let newArr = this.state.arr;
newArr.push("");
this.setState({ arr: newArr });
this.state.updater(newArr);
}
decrease() {
if (this.state.arr.length > 0) {
let newArr = this.state.arr;
newArr.pop();
this.setState({ arr: newArr });
this.state.updater(newArr);
}
}
render() {
return(
<div>
<Button onClick={ this.decrease }>decrease</Button>
<p>{ this.state.arr.length }</p>
<Button onClick={ this.increase }>increase</Button>
</div>
)
}
}
This block has two buttons in the editor's settings sidebar. One should increase the array saved in the backend, while the other should decrease it.
It has a strange behavior, though. Upon saving (while editing the site) or publishing (in a post) and then reloading the page, you'll notice that the array's length stays the same. If you click the increase button in the site editor, the save button remains disabled. And when using this block in a post, the publish button (although active) does not save the array.
I have seen similar threads, but none has a solution.
Aduth posted the solution to this problem in an issue about the setAttribute
not causing a re-render. Although the symptoms are different, the cause is the same.
As Aduth said, all "boils down to nextAttributeValue !== previousAttributeValue
." He also mentioned, "The problem is that you're mutating the attribute, not creating a new value, so the editor does not detect that a change has occurred."
To solve this issue, all it takes is to use the concat
and slice
methods instead. These JavaScript methods create new arrays instead of mutating the old ones. Replace the increase
and decrease
functions as shown.
increase() {
let newArr = this.state.arr;
newArr = newArr.concat([""]);
this.setState({ arr: newArr });
this.state.updater(newArr);
}
decrease() {
if (this.state.arr.length > 0) {
let newArr = this.state.arr;
newArr = newArr.slice(0, -1);
this.setState({ arr: newArr });
this.state.updater(newArr);
}
}