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.
ToolbarDropdownMenu
in theBlockControls
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.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 anduseState
for local state. This is a good example of the X Y problemuseState
but setting thesetAttribute
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.