I have a simple block, code below, that contains a component that fetches image data, including a URL and a pid, using a custom REST call. The registerBlockType edit method contains the component, displaying the image, and a number control. With fetch calls in the component’s componentDidUpdate() & componentDidMount() methods, when the value of the number control changes, new data is fetched and the image display updates accordingly. This works fine; the image updates and when I save using the update button in the page editor I can refresh the editor and the block loads with the updated image. However, when I try and preview the page, neither DidMount nor DidUpdate are called in the save method and the preview page shows my image failed to load message. Any idea how I deal with this?
index.js:
import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps } from '@wordpress/block-editor';
import { __experimentalNumberControl as NumberControl } from '@wordpress/components';
import {PnImage} from './PnImage.js'
registerBlockType( 'create-block/pn-test', {
attributes: {
pid: {
type: "number",
default: 8
},
},
edit: ( {attributes, setAttributes} ) => {
console.log( "Edit", attributes )
return (
<p { ...useBlockProps() }>
{"Editoor " + attributes.pid}
<PnImage pid={attributes.pid}/>
<NumberControl
label="PID"
isShiftStepEnabled={ true }
onChange={ value => { setAttributes( {pid: value})} }
shiftStep={ 10 }
value={ attributes.pid }
/>
</p>
)
},
save: ({ attributes }) => {
return (
<p { ...useBlockProps.save() }>
{ "Saved " + attributes.pid}
<PnImage pid={attributes.pid}/>c
</p>
)
}
})
PnImage.js, component code
import React, { Component } from 'react';
export class PnImage extends Component {
static defaultProps = {
size: "large",
pid: null,
}
constructor(props) {
super(props);
this.state = {
image: null,
pid: null
};
}
componentDidMount() { // initially fetch data
console.log( "DidMount, pid: ", this.props.pid)
this.Fetch()
}
componentDidUpdate() { // fetch data when props.pid changes
console.log( "DidUpdate, pid: ", this.props.pid)
this.Fetch()
}
render() {
const {image} = this.state;
return (
<p>
Image Component<br/>
{ image ? <img src={image.URL} /> : "no image found loaded for pid " + this.props.pid }
</p>
)
}
Fetch() { // fetch wrapper
if (this.props.pid != this.state.pid && this.props.pid ) {
console.log( "fetching: pid=", this.props.pid )
pnRest.Fetch('image/get', { pid: this.props.pid, size: this.props.size })
.then(data => {
if ( data ) { // image not found returns undefined
this.setState({ image: data, pid: data.id })
} else {
console.log( "Couldn't find image for pid " + this.props.pid )
}
});
}
}
}
2
Answers
After a bit of messing about, here's the solution I came up with. Working with Phil's suggestion, rather than pass setAttributes to the pnImage component, I've shifted the Fetch function into my edit function, using it in the number control callback and, so the component can be populated when it's first mounted, I pass Fetch to the component and call it in componentDidMount. Seems to work well enough.
Thanks for the help, Phil.
I don’t think you can run async functions inside the
save()
function. So it will never save the returned data. Instead what you should do is fetch the data, then save the fetched data to attributes of the block (e.g.pid
andsrc
. Then use those saved attributes in thesave()
function. You can pass thesetAttributes()
function into yourPnImage
class as a prop and access it directly:Then, within
Fetch() {}
, something like this: