Search code examples
reactjswordpressblockwordpress-gutenberg

Wordpress registerBlockType, data not fetched when saved


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 )
                    }
                });
        }

    }

}


Solution

  • 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 and src. Then use those saved attributes in the save() function. You can pass the setAttributes() function into your PnImage class as a prop and access it directly:

    <PnImage 
      pid={ attributes.pid }
      setAttributes={ setAttributes }
    />
    

    Then, within Fetch() {}, something like this:

    pnRest.Fetch('image/get', { 
        pid: this.props.pid, 
        size: this.props.size 
    }).then(data => {
        if (data) {
            this.props.setAttributes({ 
                src: data.src, 
                pid: data.id 
            });
        }
    );