skip to Main Content

I’m developing a Gutenberg Block for WordPress and I’m getting stuck with the logic.

I need to fetch some data to populate a ComboboxControl to allow the user to select one option.

So far I could make it work but when the post is saved and the page is reloaded, the saved value of the Combobox cannot match any item from the list because it is loaded afterwards and the selected value appears blank even if it’s there. If I click inside the combobox and then outside, the selected value finally shows up.

I had to put the apiFetch request outside of the Edit function to prevent endless calls to the API but I’m not sure this is good practice.

So from there I’m not sure how to improve my code or how to use hooks such as useEffect.

I need to have my data fetched and, only then, render my ComboboxControl with all the options ready.

Here is my code from the edit.js file:


 import { __ } from '@wordpress/i18n';
 import { useBlockProps } from '@wordpress/block-editor';
 import apiFetch from '@wordpress/api-fetch';
 import { ComboboxControl, SelectControl } from '@wordpress/components';
 import { useState, useEffect } from '@wordpress/element';
 import './editor.scss';

 var options = [];
 
 apiFetch( { path: '/wp/v2/posts/?per_page=-1' } ).then( ( posts ) => {
     console.log( posts );
     if ( posts.length ) {
         posts.forEach( ( post ) => {
             options.push( { value: post.id, label: post.title.rendered } );
         } );
     }
 }, options );


 export default function Edit( { attributes, setAttributes } ) {
     const blockProps = useBlockProps();
 
     const [filteredOptions, setFilteredOptions] = useState( options );
 
     const updateGroupId = ( val ) => {
         setAttributes( { GroupId: parseInt( val ) } );
     }
 
 
     return (
         <div {...blockProps}>
 
             <ComboboxControl
                 label="Group"
                 value={attributes.GroupId}
                 onChange={updateGroupId}
                 options={filteredOptions}
                 onFilterValueChange={( inputValue ) =>
                     setFilteredOptions(
                         options.filter( ( option ) =>
                             option.label
                                 .toLowerCase()
                                 .indexOf( inputValue.toLowerCase() ) >= 0
                         )
                     )
                 }
             />
         </div>
     );
 }
 

2

Answers


  1. I have the same problem…
    adding my code here too as it is slightly different and might help finding the solution

    const { registerBlockType } = wp.blocks;
    const { ComboboxControl } = wp.components;
    import { useState, useEffect } from '@wordpress/element';
    import apiFetch from '@wordpress/api-fetch';
    
    registerBlockType('hm/cptSelect', {
      title: 'cptSelect',
      category: 'common',
      icon: 'smiley',
      attributes: {
        post_id: {
          type: 'number',
          default: 0,
        },
      },
      edit: props => {
        const { attributes, setAttributes } = props;
    
        //states
        const [posts, setPosts] = useState([]);
        const [filteredOptions, setFilteredOptions] = useState(posts);
        //funcs
        const apirequiest = async () => {
          const res = await apiFetch({ path: '/wp/v2/posts' });
          const options = await res.map(post=> {
            return { value: post.id, label: post.title.rendered };
          });
          setPosts(options);
          return;
        };
        //effects
        useEffect(() => {
          apirequiest();
        }, []);
    
        return (
          <>
            <ComboboxControl
              onFilterValueChange={inputValue =>
                setFilteredOptions(
                  posts.filter(option =>
                    option.label.toLowerCase().includes(inputValue.toLowerCase())
                  )
                )
              }
              label='PostSelect'
              value={attributes.post}
              onChange={value => setAttributes({ post_id: value })}
              options={filteredOptions}
            />
          </>
        );
      },
    
      save: props => {
        const { attributes } = props;
    
        // apiFetch({ path: `/wp/v2/posts${attributes.post_id}` }).then(res => {
        //   setPostsData(res[0]);
        // });
        return (
          <>
            <div>{attributes.post_id}</div>
          </>
        );
      },
    });
    
    Login or Signup to reply.
  2. I think you need some useEffect with your API results as a dependency, that did work for me, something like

      const [filteredOptions, setFilteredOptions] = useState();
      useEffect(() => {
        setFilteredOptions(options);
      }, [options, setFilteredOptions]);
    

    Likely you would need a conditional around your Control, ie

    { filteredOptions ? (
       <ComboboxControl...
       />
       ) : null }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search