0

In order to be able to use React lifecycle methods I'm trying to change the default Edit function created by @wordpress/create-block into a javascript class like in this simple test example:

import { registerBlockType } from '@wordpress/blocks';

/**
 * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
 * All files containing `style` keyword are bundled together. The code used
 * gets applied both to the front of your site and to the editor.
 *
 * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
 */
import './style.scss';

/**
 * Internal dependencies
 */
//import Edit from './edit';
import save from './save';
import icons from "../../icons";
import {Component} from "@wordpress/element";
import {useBlockProps} from "@wordpress/block-editor";

export default class TestClass extends Component {
    render() {

        return (
            <div {...useBlockProps()}>
                This is only a test!!
            </div>
        );
    }

}

/**
 * Every block starts by registering a new block type definition.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
 */
registerBlockType('multiple-blocks-plugin/notice', {
    icon: icons.note,

    /**
     * @see ./edit.js
     */
    edit: TestClass, //Edit,

    /**
     * @see ./save.js
     */
    save,
});

That's an approach I've seen in some tutorials like this one but, for some reason, I'm not been able to do it. The above code, gives me the following error at the browser console:

react_devtools_backend.js:2540 Error: Minified React error #321; visit https://reactjs.org/docs/error-decoder.html?invariant=321 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
    at Object.S (react-dom.min.js?ver=16.13.1:84)
    at d.useContext (react.min.js?ver=16.13.1:31)
    at xu (block-editor.min.js?ver=fc8c27c6e95e7e0d59b4e344cb9ddfed:12)
    at TestClass.render (index.js:30)
    at Ie (react-dom.min.js?ver=16.13.1:104)
    at rh (react-dom.min.js?ver=16.13.1:103)
    at zj (react-dom.min.js?ver=16.13.1:228)
    at Th (react-dom.min.js?ver=16.13.1:152)
    at tj (react-dom.min.js?ver=16.13.1:152)
    at Te (react-dom.min.js?ver=16.13.1:146)

Digging for a solution, I've found out that the problem is caused by the function useBlockProps. Is there a way to avoid it without loosing the useBlockProps provided functionalities?

So now I have the following questions (all related to the problem explained above):

  • Am I doing something wrong? I don't see in the WordPress docs an advice to not use the useBlockProps function when working with classes but I am new to Wordpress development so I'm not sure.
  • Is it not allowed anymore?
  • Is there a better alternative to be able to use the React lifecycle?

EDIT: The rendering component state problem

The question above arouse from my research to solve a component state problem I have found. @TomJNowell stated that the problem I have is not related to state, so I'm posting a simplification of my code to only address the original problem that led me to research about dealing with state on WordPress block components.

edit.js:

export default function Edit({attributes, setAttributes}) {
    const getClassName = (status) => status === 'info'? 'mbp-black' : 'mbp-red';
    const getSelectedIcon = (status) => status == 'info' ? icons.info : icons.error;

    return (
        <div{...useBlockProps({className: getClassName(attributes.status) })}>
            <BlockControls>
                <ToolbarDropdownMenu hasArrowIndicator
                                     icon={ getSelectedIcon(attributes.status) }
                                     label="Status"
                                     controls={[
                                         {
                                             icon: icons.info,
                                             title: 'Info',
                                             onClick: () => setAttributes({status: 'info'}),
                                             isActive: attributes.status === 'info'
                                         },
                                         {
                                             icon: icons.error,
                                             title: 'Error',
                                             onClick: () => setAttributes({status: 'error'}),
                                             isActive: attributes.status === 'error'
                                         }
                                     ]}
                />
            </BlockControls>
            <p >
                {__(
                    'I have state!!!!',
                    'multiple-blocks-plugin'
                )}
            </p>
        </div>

    );

What I want to achieve with this code is, that immediately after the user chooses Info or Error on the dropdown menu, the component on the editor be re-rendered with the equivalent css class and also if I've selected the Error option, the next time I open the menu, this should be the selected option on the menu.

16
  • What kind of lifecycle methods are you trying to use? You shouldn't need to convert the component to the old class based components to do things, if you'd shared what you were trying to do it would be possible to write an answer that used react knowledge to do it without any component classes
    – Tom J Nowell
    Nov 28, 2021 at 20:27
  • @TomJNowell "Old class based components"?... Class based components are discouraged on new gutenberg blocks? I'm trying to be able to use setState to mantain the component state. I have a ToolbarDropdownMenu in the BlockControls that sets a attribute that is the base for a class name <div className={getCssClasses(attributes.status)}>. When I chose one item from the menu, I want the div from the component to immediately assume the new class so the component should render again to reflect it.
    – Iogui
    Nov 28, 2021 at 20:41
  • No not Gutenberg, React in general, but I do not see why this would require lifecycle methods, have you not tried to use useState to set state? If any state or props change the component is re-rendered, that includes block attributes. What you're trying to implement is how React already works, you don't need a class based component or any of the lifecycle methods to do this, just to set the attributes when they change and useState for local state. This is a good example of the X Y problem
    – Tom J Nowell
    Nov 28, 2021 at 20:44
  • Instead, I recommend that you ask how to use an attribute in a selection toggle in a blocks toolbar. That's your actual problem, and it has a simple solution that has nothing to do with this question
    – Tom J Nowell
    Nov 28, 2021 at 20:46
  • @TomJNowell I'm pretty new to React too but the official tutorial "Intro to React" uses class based components so it's natural that I'm trying to use it. No I didn't even knew about useState but setting the setAttribute is not re-rendering the component. Thank you for your tip on using attribute ia a selection toggle, I will make some search on it before asking.
    – Iogui
    Nov 28, 2021 at 20:52

1 Answer 1

1

It turned out that, at least for my specific use case, there was no need to use a class or interact with React lifecycle methods. My code was already working with the component as a function but for some reason I wasn't able to see it in that moment. I have probably made a mistake while testing.

But I've made some research an discovered that the guys at React have an argument against using class in javascript. To address things that could just be done through classes in the past they have created the hooks functionality to be used by functional React components. If you have the need to interact with the React state, take a look at the new state hook as everything seems to be functional now on at modern React applications. This would be my next step since even the guys from React are advising against using classes on new React components. So now I will answer my own questions with what I've found in my research in order to help some one that really needs this:

Digging for a solution, I've found out that the problem is caused by the function useBlockProps. Is there a way to avoid it without loosing the useBlockProps provided functionalities?

At the present moment it seems to be no way to use classes and still have the useBlockProps functionality since it seems not to be implemented for classes by the folks from Wordpress and React Hooks doesn't work with classes.

Am I doing something wrong?

Yes, I had no need to use a class nor to direclty interact with React lifecycle methods since my functional code was already working.

Is it not allowed anymore?

useBlockProps is a hook so it was probably created targeting functional components only. So, it will not be able to be used with React classes components but React classes components can still be used. To use WordPress React classes now will be a little bit difficult though since the WordPress folks seems not to be supporting it.

Is there a better alternative to be able to use the React lifecycle?

Yes, it seems that it can be done now from a functional component with the useState hook that can be imported directly from the @wordpress/element package the source code seems to just be importing it directly from React with no modification. Do it if you have the need to do it but do take care of the possible colateral effects.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.