• I’m developing my first Gutenberg block under my theme, and I ran into an interesting behaviour which I would like to understand.

    I’m making a block that creates special tables. The user can create rows, columns, fill and sort them.

    I am using the Component class to get access to React lifecycle methods, and during development I decided to use state for all the UI stuff and only trigger setAttributes when I make changes that I want to be reflected in the database. (I’m only saving data to the db by defining the save function as an empty function that returns null).

    When I got to implementing the row sorting functionality I noticed that if I save the post after sorting, the changes were reflected in the database even though I never ran setAttributes. So the correct version of the table rows were only in the state, and not in the attributes.

    I tried removing setAttributes alltogether, but then a new block would never save any changes. So it seems that setAttributes is at least needed to create the initial save.

    Could someone point me to the correct direction where I could find information on how this is happening? I want to understand how state and attributes relate to each other. I tried looking through the documentation but couldn’t find any information on this.

Viewing 5 replies - 1 through 5 (of 5 total)
  • You might do better to move this to the WordPress development forum. Gutenberg has its own presets for babel and its own version of redux. I’ve only set up the environment once manually. I usually just use the Create-block. It creates the react environment to the point where you just need to work with the jsx. You might also want to check out the GitHub Gutenberg repository. It lists the code for the project and all the blocks and documentation. Not sure if this is of any help.

    • This reply was modified 2 years, 8 months ago by mrtom414.
    Thread Starter ebbec

    (@ebbec)

    I used the wordpress cli to setup the environment, so I believe it should be the same.

    I guess looking into the Gutenberg repo is the way to go, but it looks like it will take some time to understand the structure there. I can’t even find the function “setAttributes” in the entire repo ??

    Thanks for the reply. I’ll try to dig around a bit.

    Have you looked at the register block type function. I believe the attributes are set in it. Its one of the options when you register the block. Also you should have an edit and save function. Edit being for the blocks in the editor and save being for the database saves. I haven’t done that much in react it still kind of new to me. Just started playing with it when I started looking into building blocks. The save and edit functions have to share the same attributes. Try checking this page I believe all your attributes have to be registered in this function

    `attributes (optional)
    Type: Object
    Attributes provide the structured data needs of a block. They can exist in different forms when they are serialized, but they are declared together under a common interface.

    // Specifying my block attributes
    attributes: {
    cover: {
    type: ‘string’,
    source: ‘attribute’,
    selector: ‘img’,
    attribute: ‘src’,
    },
    author: {
    type: ‘string’,
    source: ‘html’,
    selector: ‘.book-author’,
    },
    pages: {
    type: ‘number’,
    },
    },`

    Thread Starter ebbec

    (@ebbec)

    EDIT: Actually looks like registerBlockType is exactly where the store is created by the name given in the meta data. Now to find where it hooks up with the state then ??

    ORIGINAL:
    I don’t think that’s what I’m looking for. It seems there’s an abstraction layer between wordpress and react which handles the state/store in some way which is not apparent here. The attributes are fed as props to the component which makes me think that there is a higher level redux store or something similar. I assumed that the database would only reflect what happens in that store. This is why I thought it was safe to use the components internal state freely without needing to worry about the db. But this does not seem to be the case.

    I can easily work around this, so it is not currently an issue per se. But I would like to understand how wordpress is aware of the internal state of component when hitting save.

    Here’s a very much simplified version of the code that I’m working on. The only two uses of setAttributes are reflected here.

    import { registerBlockType } from "@wordpress/blocks";
    import { Component } from "@wordpress/element";
    import { useBlockProps } from "@wordpress/block-editor";
    import metadata from "./block.json";
    
    class CustomTable extends Component {
    	constructor(props) {
    		super(...arguments);
    		this.props = props;
    
    		this.state = {
    			rows: [[""]],
    			sortRows: {},
    			error: "",
    		};
    		// ...etc...
    	}
    
    	componentDidMount() {
    		let rows = this.props.attributes.rows;
    		this.setState({
    			rows: this.props.attributes.rows,
    		});
    	}
    
    	updateRows(val, row, col) {
    		let temp = [...this.state.rows];
    		temp[row][col] = val;
    		this.setState({ rows: temp }, () => {
    			////////
    			// HERE I AM SAVING ATTRIBUTES
    			////////
    			this.props.setAttributes({
    				rows: this.state.rows,
    			});
    		});
    	}
    
    	// add/remove rows/columns etc... all of which only affect the STATE not the ATTRIBUTES
    
    	triggerSort(rowsInCorrectOrder) {
    		this.setState({ rows: rowsInCorrectOrder, sortRows: {} }, () => {
    
    			this.props.setAttributes({
    				rows: rowsInCorrectOrder,
    			});
    			//^^^^^^
    			// NO MATTER IF THIS IS HERE OR NOT. IT WILL SAVE THE CORRECT ORDER TO DB.
    			////////
    		});
    	}
    
    	updateOrder() {
    		let rowsInCorrectOrder = functionWhichGetsTheRowsInCorrectOrder();
    		this.triggerSort(rowsInCorrectOrder);
    	}
    
    	render() {
    		return (
    			<div>
    				{this.state.rows.map((row, rowIndex) => {
    					return (
    						<div>
    							<input
    								type="number"
    								value={this.getCurrentValue(rowIndex)}
    								onChange={(e) => {
    									this.updateOrder(rowIndex, e.target.value);
    								}}
    								onBlur={this.triggerSort}
    							/>
    							{row.map((item, colIndex) => {
    								return (
    									<input
    										type="text"
    										value={col}
    										onChange={(e) => {
    											this.updateRows(
    												e.target.value,
    												rowIndex,
    												colIndex
    											);
    										}}
    									/>
    								)
    							})}
    						</div>
    					);
    				})}
    			</div>
    		);
    	}
    }
    
    registerBlockType(metadata, {
    	edit: function (props) {
    		return (
    			<div {...useBlockProps()}>
    				<CustomTable {...props} />
    			</div>
    		);
    	},
    
    	save: function (props) {
    		return null;
    	},
    });
    • This reply was modified 2 years, 8 months ago by ebbec.
    • This reply was modified 2 years, 8 months ago by ebbec.

    It sounds like you have what you need now. I think you are more advanced in React development than I am. I’ve only created a few blocks but need to work at it a little more. Hope it is enough to help you complete your project.

Viewing 5 replies - 1 through 5 (of 5 total)
  • The topic ‘Gutenberg block updating data without setAttributes’ is closed to new replies.