古腾堡块-如何在属性更改后强制更新?

时间:2020-05-12 作者:ejoo

我构建了一个定制的Gutenberg块,以显示一个名为story. 大多数事情最终都很顺利,故事通过服务器端渲染(ServerSideRendering)按需要显示出来。要选择要显示的故事,侧边栏中有一个单选按钮列表。

我想要的:-从单选按钮中选择一个故事后,我希望显示的故事刷新。从一些例子中,我相信这可以解决withSelectuseSelect 或者类似的东西。但我不知道该如何在我的案件中使用它们。

story_id 如果编辑函数的story_id.

代码如下:

/**
* Story-Block: osf-blocks-story
*
* Gives back a rendered story (list of images with text & title) after selecting from the left block sidebar
*
* based on create-guten-block
*/

import \'./style.scss\';
import \'./editor.scss\';

import { registerBlockType } from \'@wordpress/blocks\';
import { ServerSideRender, RadioControl, PanelBody } from \'@wordpress/components\';
import { InspectorControls } from \'@wordpress/block-editor\';
import { Component, Fragment } from \'@wordpress/element\';
import { useSelect, withSelect } from \'@wordpress/data\';


let $ = jQuery;

// short for console.log
function cl(words) { console.log(\'drx-osf-story, block.js: \' + words); }

// to get more object infos than console.log(obj + \'some comment\');
function ol(object, words=\'\') {
   cl(words + \': \\\\/\');
   console.log(object);
}

// Represents a single story, i.e. a collection of parts with image, short text and possibly a title
class Story extends Component {
   constructor(props) {
       super(props);
       this.state = {
           story_id: props.story_id
       }
   }

   render() {
       return (
           <ServerSideRender
               block="osf-blocks/story"
               attributes={this.state}
           />
       )
   }
}

// Represents a list of radiobuttons to select one of all stories
class StoriesListControl extends Component {

   constructor(props) {
       super(props);
       this.state = {
           changeHandler: props.changeHandler,
           story_id: props.story_id,
           options: props.options
       };
   }

   render() {
       return (
           <InspectorControls>
               <PanelBody title=\'Story auswählen:\'>
                   <RadioControl
                       label="Wähle, welche Story hier eingefügt werden soll."
                       help="Die Story muss vorher über das Story-Interface angelegt worden sein."
                       selected={ this.state.story_id }
                       options={ this.state.options }
                       onChange={ this.state.changeHandler }
                   />
               </PanelBody>
           </InspectorControls>
       );
   }
};


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

   title: \'Story\' ,
   icon: \'images-alt2\',
   category: \'widgets\',
   attributes: {
       story_id: {
           type: \'string\'
       }
   },
   keywords: [
       \'osf-blocks-story\',
       \'Story\',
       \'Stories\'
   ],


   edit: (props) => {

       // what to do with withSelect/useSelect here??

       const {
           attributes: { story_id },
           setAttributes
           } = props;

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

       // fetching the list of all stories (custom post type) from server
       const getStoriesList = () => {
           let answer;
           $.ajax({
               url: \'/wp-admin/admin-ajax.php\',
               type: \'POST\',
               data: {
                   action: \'stories_list\'
               },
               async: false,
               timeout: 3000 // ms
           }).done((rspns) => {
               // removing trailing 0  - what does it mean?
               answer = { status: \'success\', result: JSON.parse(rspns.substr(0, rspns.length - 1)) };
           }).fail((xhr, status) => {
               cl(\'request failed: \' + status);
               ol(xhr);
               answer = { status:\'fail\',  log: status }
           });
           return answer;
       };

       // tie it all together
       let response = getStoriesList();
       let output;

       if ( \'success\' == response.status) {
       // AJAX for storieslist succeeded

           ol(response, \'response\');

           output = (
               <Fragment>
                   <StoriesListControl
                       changeHandler={ changeHandler }
                       story_id={ story_id }
                       options={ response.result }
                   />
                   <div className="story-container">
                       <div id={ "story" + story_id } className="story-preview container">
                           <Story story_id={ story_id } />
                       </div>
                   </div>
               </Fragment>
           );

       }
       else if (\'fail\' == response.status) {
       // AJAX failed
           output = (
               <Fragment>
                   <div className="story-container">
                       <p>No connection to the server: { response.log }</p>
                   </div>
               </Fragment>
           );
       }

       return output;
   },

   save: (props) => {
       return null;
   }

});

1 个回复
SO网友:bosco

在本例中,您将异步jQuery AJAX调用视为一个同步函数,从而导致getStoriesList() 持续返回undefined 函数在调度HTTP请求后立即返回;正在尝试访问response.status 应持续投掷ReferenceError: response is not defined.

在几乎所有情况下,通过古腾堡的各种数据存储上提供的选择器访问数据更为理想-如果REST API在存储中丢失或未初始化,许多选择器将从REST API异步获取数据,然后更新各自的状态,并自动触发已订阅该状态更改的每个组件的更新,使用withSelect()useSelect().

在这里绕过Gutenberg的数据流,您无法在使用新数据时更新组件useSelect()withSelect() 因为AJAX处理程序的响应不会触发存储中的状态更新,因此存储不会通知任何订阅的函数或组件。当前的实现还导致在每次呈现组件时调度HTTP请求,这是不必要的。

在这种情况下getStoriesList() 返回CPT帖子列表的函数和关联的AJAX处理程序应简洁地替换为getEntityRecords() 上的选择器core 数据存储。我们可以通过返回的对象访问核心数据存储的选择器select( \'core\' ). 将该访问包装在useSelect() 钩子创建对核心存储的订阅,重新呈现edit() 每当所选数据的状态发生更改时自动执行。

此外,第二个论点useSelect() 是一个依赖项数组。通过添加story_id 对于它,我们可以缓存帖子列表,并且只有在story_id 更改。

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

  // ...
});

相关推荐