Using Gutenberg block components in admin interface controls

Warning (or note to other readers): Lots of JavaScript code ahead.. in fact, there’s no PHP here. So I hope you already have some JavaScript skills prior to reading this answer. 🙂 Thanks!

I’ve also got a filter in place that sets the script type of my .js
file to text/babel so JSX interprets correctly. I don’t think I’ve
missed anything.

But for production usage, you should build your script or don’t use the JSX syntax in your code because browsers don’t understand the syntax without a compiler’s help. Also, in production or a “standard”/live WordPress site, you should not use the text/babel type, hence there’s no need to load/include the Babel compiler because even React itself says that the compiler makes your site slow. So use it for development and testing purposes only.

Update Mar 16 2020 (regarding above statement): No, I did not mean it’s not recommended to use JSX in your code. In fact, I do
recommend it because it’s easier to, for example create a strong
element using JSX than “the normal way” using
wp.element.createElement() in Gutenberg or React.createElement()
in native React apps:

  • Creating a strong element in JSX:

    <strong className="foo">
      It's easy, pretty much like regular HTML markup.
      But you can use JS expressions: 2 * 5 = { 2 * 5 }.
    </strong>
    
  • Creating a strong element “the normal way” (non-JSX):

    // In native React, you'd use React.createElement().
    // Not super hard, but how about nested elements?
    wp.element.createElement( 'strong', {
      className: 'foo'
    }, 'Some text here. 2 * 5 = ', 2 * 5 );
    

And the thing that I do not suggest is including an in-browser JSX
preprocessor like
Babel

on your site. If that’s not what you’re referring to, I’m sorry, but
still, what I was really saying is, if you use JSX in your script,
then it’s highly recommended to build the script prior to using it on
a production/live site. But you can opt not to develop in JSX, if
you want to or for simple stuff that does not involve complex
elements.

I hope that makes sense now? (Note: I reworded the above paragraph
on Apr 02 2020.)

The issues in your code

  1. The MyCheckboxControl issue is not your fault, but unfortunately, the Gutenberg team made a mistake in their example. The proper code should be:

    Update Apr 03 2020: So the code was corrected on March 17, 2020. But today, I noticed it’s there again. 🙁 So I’m no longer going to
    say it’s fixed, even if it’s already fixed. Instead, for the code in
    the question, the below is the proper one.

    const MyCheckboxControl = () => {
        const [ isChecked, setChecked ] = useState( true );
        return (
            <CheckboxControl
                heading="User"
                label="Is author"
                help="Is the user a author or not?"
                checked={ isChecked }
                onChange={ setChecked }
            />
        );
    };
    
  2. You can’t pass JSX to wp.element.createElement():

    Update Apr 02 2020: Actually you can; just make sure it’s done properly — and if you’re not including any JSX preprocessor in
    your site, then be sure to build the script so that the JSX is
    transformed to the format understood by most browsers. And to @Andrew,
    I mainly revised this answer so that others don’t get a false info
    because of the above statement. 🙂

Additional Notes (In response to your answer)

So you’re right about that const { CheckboxControl } = wp.editor; //incorrect, which I did not notice because I directly used wp.components.CheckboxControl.

However, the following:

const el = wp.element.createElement;
const setState = wp.data.setState;
const withSelect = wp.data.withSelect;
const withDispatch = wp.data.withDispatch;
const { CheckboxControl } = wp.editor;
const { RichText } = wp.editor;
const { useState } = wp.element;
const { TextControl } = wp.components
const { withState } = wp.compose;

can be made simpler:

const el = wp.element.createElement;
const { setState, withSelect, withDispatch } = wp.data;
const { CheckboxControl, TextControl } = wp.components;
const { RichText } = wp.editor;
const { useState } = wp.element;
const { withState } = wp.compose;

I.e. Use const { <property>, <property>, <property>, ... } = myObject; where <property> is the property name in whatever the object is. Exception to the el since wp.element.el is undefined.

Creating React/Gutenberg components

You can actually check the React’s official docs like this for creating components and working with the component’s prop(ertie)s. And for examples, the following are (using JSX and) all valid:

// Using normal function.
function Welcome( props ) {
  return <h1>Hello, {props.name}</h1>;
}

// Using the class method.
class Welcome extends wp.element.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

// Using "arrow" function.
const Welcome = ( props ) => <h1>Hello, {props.name}</h1>;

// No props, so here it's OK to use no function wrapping.
const Welcome = <h1>Hello <i>World</i>!</h1>;

// Note: If there are DIRECT child elements, wrap in a container like a Fragment.
const Welcome =
    <>
        <h1>Hello World!</h1>
        <p>Foo bar baz</p>
    </>
;

And for those who don’t know or not yet familiar with the so-called “arrow function” (in ESNext/ES6), please check Arrow function expressions on MDN.

Accessing component properties/props

As with the function/component props which is really a function parameter/argument, whether it’s React, Gutenberg, Vue, jQuery, etc. library, you just access the function parameters the same way. And sorry, I can’t give a tutorial here on the different ways to access the parameters, but the MDN reference would be your best friend. Nonetheless, I hope these (very simple) examples help you:

// Log all parameters passed to the function. Similar to func_get_args() in PHP.
function foo() {
    console.log( ...arguments );
}

function foo( props ) {
    console.log( props );
}

// You should pass an object to the function.
function foo( { a, b } ) {
    console.log( { a, b } );
}

// All of the above show {a: 1, b: "two"} when you do:
foo( { a: 1, b: 'two' } );

function foo2( a, b, ...more ) {
    console.log( a, b, more );
}
// This one would show: 1 "two" (3) [3, "four", false]
foo2( 1, 'two', 3, 'four', false )

And here’s an example for a class-based component, where you’d use this.props to access the component’s props, although you can really use any of the above methods to access the props:

class Foo extends wp.element.Component {
    constructor() {
        super( ...arguments ); // you must call super()
        console.log( this.props );
    }
    /* You can also do so:
    constructor( props ) {
        super( ...arguments );
        console.log( props, this.props === props ); // the condition should return true
    }

    // Or like so:
    constructor( { name, place } ) { // unpack the props
        super( ...arguments );
        console.log( name, place );
    }
    */

    render() {
        const { name, place } = this.props;
        return <h1>Hello, { name } from { place }!</h1>;
    }
}

// The following JSX would output <h1>Hello, John from Pluto!</h1>
// And in the console, you'd see: {name: "John", place: "Pluto"}
//<Foo name="John" place="Pluto" />

Colophon

So I hope this (revised) answer helps you (more) and really, when it comes to Gutenberg, your best pals are the React’s official site, MDN web docs for JavaScript and of course, the Gutenberg (block editor) handbook.

Happy coding!

Leave a Comment