gutenberg block – how to force update after attribute changed?

In this instance, you are treating the asynchronous jQuery AJAX call as a synchronous function, resulting in getStoriesList() consistently returning undefined as the function returns immediately after dispatching the HTTP request; trying to access response.status should be consistently throwing ReferenceError: response is not defined.

In almost all cases it is more ideal to access data through the selectors provided on Gutenberg’s various data-stores – many selectors will asynchronously acquire data from the REST API if it is missing or uninitialized in the store, then update the respective state, and automatically trigger updates for every component which has subscribed to changes to that state using withSelect() or useSelect().

In circumventing Gutenberg’s data-flow here, you are unable to update the component when new data comes in using useSelect() or withSelect() because the response from your AJAX handler does not trigger a state update in the stores, and thus the stores will not notify any subscribed functions or components. The current implementation also results in an HTTP request being dispatched every time the component is rendered, which is unecessary.

In this case, the getStoriesList() function and associated AJAX handler returning a list of CPT posts should be succinctly replaced with the getEntityRecords() selector on the core data store. We can get access to the core data store’s selectors via the object returned by select( 'core' ). Wrapping that access up in a useSelect() hook creates a subscription to the core store, re-rendering edit() automatically whenever the data being selected changes in state.

Further, the second argument of useSelect() is an array of dependencies. By adding story_id to it, we can cache the post list and only retrieve a new one when story_id changes.

// Registering the block
registerBlockType('osf-blocks/story', {
  //...

  edit: (props) => {
    const {
      attributes: { story_id },
      setAttributes
    } = props;

    const stories = useSelect(
      select => select( 'core' ).getEntityRecords(
        'postType',
        'story',
        { id: story_id }
      ),
      [ story_id ]
    );

    // called, if a radiobutton gets selected
    const setStoryID = ( story_id ) => setAttributes( { story_id } );

    return (
      <Fragment>
        <StoriesListControl
          changeHandler={ setStoryID }
          story_id={ story_id }
          options={ stories }
        />
        <div className="story-container">
          <div id={ `story${ story_id }` } className="story-preview-container">
            <Story story_id={ story_id } />
          </div>
        </div>
      </Fragment>
    );
  }

  // ...
});

Leave a Comment