Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wordpress Gutenberg: React components on front end

Gutenberg is still pretty new, but I'm still hoping someone has encountered this issue and found a solution.

I've used create-guten-block to boilerplate a project and created a test block. The problem I'm running into is that when I try to use a React component to modify state on the front-end, nothing happens. The components load fine through save(), but when you try to do something simple like toggle a list, the front-end remains unresponsive to the state changes. I'll also note that create-guten-block doesn't load any front-end JS, so I swapped the compiled javascript to load on the front end and still haven't been able to get it working.

Here's some code I pulled from Codecademy as an easy example to test with. When you select a name, it changes the text in sibling.js to display the name. The code works fine in create-react-app but does nothing on the front-end as a block in Gutenberg:

block.js

import { Parent } from './parent';

// More code here 

save: function( props ) {
    return (
          <div>
              <Parent />
          </div>
     );
 },

parent.js

import React from 'react';
import { Child } from './child';
import { Sibling } from './sibling';

export class Parent extends React.Component {
    constructor(props) {
        super(props);

        this.state = { name: 'Frarthur' };

        this.changeName = this.changeName.bind(this);
    }

    changeName(newName) {
        this.setState({
        name: newName
        });
    }

    render() {
        return (
        <div>
            <Child onChange={this.changeName} />
            <Sibling name={this.state.name} />
        </div>
        );
    }
};

child.js

import React from 'react';

export class Child extends React.Component {
    constructor(props) {
        super(props);

        this.handleChange = this.handleChange.bind(this);
    }

    handleChange(e) {
        const name = e.target.value;
        this.props.onChange(name);
    }

    render() {
        return (
        <div>
            <select
            id="great-names"
            onChange={this.handleChange}>

                <option value="Frarthur">Frarthur</option>
                <option value="Gromulus">Gromulus</option>
                <option value="Thinkpiece">Thinkpiece</option>
            </select>
        </div>
        );
    }
}

sibling.js

import React from 'react';

export class Sibling extends React.Component {
    render() {
        const name = this.props.name;
        return (
        <div>
            <h1>Hey, my name is {name}!</h1>
            <h2>Don't you think {name} is the prettiest name ever?</h2>
            <h2>Sure am glad that my parents picked {name}!</h2>
        </div>
        );
    }
}
like image 880
iskotaa Avatar asked Aug 09 '18 15:08

iskotaa


Video Answer


2 Answers

Gutenberg is awesome but can't say the same thing for its documentation. It is like any other Wordpress documentation, way too verbose and ill-organized and chatty. I think it comes with the territory and target consumers.

It took me some time and several pass over the handbook to understand what blocks are. Resources are scarce and people usually confuse blocks with their on screen visual representation in the editor.

That being said, lets start with what Gutenberg blocks are. Gutenberg blocks are graceful shortcodes like this one:

<!-- wp:paragraph {"key": "value"} -->
<p>Welcome to the world of blocks.</p>
<!-- /wp:paragraph -->

These shortcodes are rendered into react components by the editor for visual representation.

Remember the shortcodes like this one here, [gallery id="123" size="medium"] which could be rendered into visual representation by tinymce plugin and you would see fully formed and functioning gallery in the editor window. The idea is same, ony this time Gutenberg editor renders slightly different shortcodes into a visual representation.

Now, the confusion arises because WordPress docs addresses these visual representations also as blocks. But source of truth for the whole rendering-serializing-parsing-re-rendering cycle is one and that is these so called "graceful shortcodes", rest is different forms and representations these shortcodes take. Say, in editor context, its is a rendered react component, on front end it is just regular html.

It is the return element of edit function that will determine how a block will appear in the editor window:

<!-- language: lang-js -->
registerBlockType(«NAMESPACE/BLOCK_NAME», {
    edit: function(props){
        // Should return a react element
    }
})

It is paramount to understand a block's life cycle to understand them better. Lets start from the beginning:

When you click a block icon in components panel, return of save function will be rendered and serialized and inserted into the page.

<!-- language: lang-js -->
registerBlockType("NAMESPACE/BLOCK_NAME", {
    save: function(props){
       // Should return a react element
    }
})

save function should return a react element, this element will be rendered by react and serialized by the block serializer and inserted into to the post content as a block. You can check serializer.js for how React element serialized into a block1.

<!-- wp:image -->
<figure class="wp-block-image"><img src="source.jpg" alt="" /></figure>
<!-- /wp:image -->

If it is dynamic block, save function will return null, so there will no content. Block will look something like this:

<!-- wp:latest-posts {"postsToShow":4,"displayPostDate":true} /-->

Notice the self closing comment:

In blocks grammar, the first one is called a static block (_Block_Balanced_) and the second one is dynamic block (_Block_Void_).

It is important to note that static blocks contain rendered content and an object of attributes. For dynamic blocks, render_callback should be supplied to register_block_type function during block registration.

So, when the_content is requested, server gets the_content and passes it through several filters before responding the request.

During this phase, attributes will be strip off from static blocks and content will be returned since static blocks have their contents already in themselves. For dynamic blocks, render_callback will be invoked and its return value will be returned as block content. That's what they mean by somehow fully-isomorphic3 in the documentation. You can check out render_block function in Wordpress core.

When you edit the block via gutenberg's visual elements, the block will go through re-serialization process all over again and a new visual representation will be drawn onto page based on changes you've made.

<!-- wp:paragraph {"key": "value"} -->
<p>Welcome to the world of blocks.</p>
<!-- /wp:paragraph -->

Once you click the publish button, this serialized data or row data, as the documentation tells, will be save to database.

Lets say you close the page after saving. When you opened it next time, saved block will be parsed by the block parser and visual representation will be drawn onto page. You can take a look at the parser2.

During parsing, block markup will be validated against the save function. If you changed the return of save function between two edits, previously saved block markup will be invalid or deprecated. You can update deprecated code by providing upgrade path in your block setting in registerBlockType. However you change the edit function without repercussions since it controls how blocks will appear on editor screen.

Upgrade path is just an array of objects with functions and properties. Deprecated block will be checked by each element on this array, based on priority and will be migrated if block is compatible with new version, if not old version will be returned.

Now coming to your question, when request a page on the front end server will send you fully formed html. So, on the front what you get is is static html, not a react element.

So, in reality save function has nothing to do with frontend other than creating static html wrapped in block comments <!-- wp:image --><!-- /wp:image -->, which happens while editing content. It is never run or consulted when serving the_content on the front end.

In order to add interactivity, you have to write code specifically targeting the front-end, just as you did before Gutenberg.

You can see the_content in its plain text form by switching to Code Editor in the editor window using More tools and options button, vertical ellipses next to update button.

From the front-end perspective, there is no difference whether you use tinymce editor or Gutenberg editor or plain html when creating the_content.

It is up to you enqueue another javascript file or use the one you enqueued via enqueue_block_assets when registering block.

To use React, you have to mount your component onto document using ReactDOM. Good thing is Wordpess already provides React and ReactDOM in global space but you need to indicate the dependency when enqueuing the script.

like image 177
snnsnn Avatar answered Nov 15 '22 20:11

snnsnn


It is my understanding that Gutenburg only supports interactivity on the editor side of the equation. Yes, you define the view side in React, but this view is rendered to static markup at the point the post is saved, and then only this static markup is delivered to the client (readers).

The benefit is you get "server side rendering" for free, and your client doesn't need React to view the post. The downside is if you want to do client side interactivity, its outside the scope of Gutenburg, and you'll have to find your own way.

like image 35
Bessey Avatar answered Nov 15 '22 19:11

Bessey