• I have a strange error in a block which I’m developing, I made a short video to show what’s going on:
    https://share.vidyard.com/watch/wtP2yr8ajJRgHRmDa8823f?

    Initially, you’ll see a couple of blocks on the page with their content displayed. I then install a 3rd party text block and as you can see just doing that destroys the content and displays the following message:
    “This block has encountered an error and cannot be displayed”

    The 3rd party text block is the only one that causes this error in my block as far as I know.

    I can’t imagine why another block would interfere with mine.

    If I look at the console, I can see the following:

    react-dom.min.js?ver=17.0.1:141 TypeError: Cannot read properties of undefined (reading 'length')
    
        at Ke (react-dom.min.js?ver=17.0.1:99:310)
        at Object.vh [as useCallback] (react-dom.min.js?ver=17.0.1:108:485)
        at c.useCallback (react.min.js?ver=17.0.1:29:406)
        at xe (data.min.js?ver=c5f9378263e5eea9d870764c8fc1dd1b:2:21762)
        at w (block-editor.min.js?ver=54657317be78fb1c246c72b38dc2ed0e:26:78595)
        at Edit (edit.js:<strong>202</strong>:21)

    Line 202 is '{...useBlockProps({'

    I’m using useBlockProps as follows:

    const myClasses =${backgroundColor.class} netmonics-xs-is-${ColumnsXS}-columns netmonics-sm-is-${ColumnsSM}-columns netmonics-md-is-${ColumnsMD}-columns netmonics-lg-is-${ColumnsLG}-columns netmonics-xl-is-${ColumnsXL}-columns netmonics-xxl-is-${ColumnsXXL}-columns;
    
        return (
            <div
                {...useBlockProps({
                    className: myClasses,
                })}
            >

    I use the variable myClasses to add some additional classes.

    I tried removing useBlockProps() and adding the classes it adds into the myClasses variable and the error goes away. Obviously, I need to use blockprops and it’s a standard feature to use so I don’t think I’m doing anything wrong.

    So I am mystified as it seems useBlockProps() is causing the error.

    I’m completely stuck so just wondered if anyone has any ideas?

    Best Regards,

    Steve

    • This topic was modified 2 years, 6 months ago by Yui.
    • This topic was modified 2 years, 6 months ago by Steve.
    • This topic was modified 2 years, 6 months ago by Jan Dembowski.
Viewing 7 replies - 1 through 7 (of 7 total)
  • Hi it looks like myClasses might be causing the issue. The docs seem to suggest it’s expected to be a single classname rather than a list https://developer.www.ads-software.com/block-editor/reference-guides/block-api/block-edit-save/#block-wrapper-props

    • This reply was modified 2 years, 5 months ago by Adam Wood.
    • This reply was modified 2 years, 5 months ago by Adam Wood.
    Thread Starter Steve

    (@bugdens)

    Hi Adam,

    Thank you for the idea.

    It doesn’t work unfortunately, I got errors with the above line, tried putting it in ticks i.e. ` but still didn’t work.
    const myClasses =<code>${backgroundColor.class} netmonics-xs-is-${ColumnsXS}-columns netmonics-sm-is-${ColumnsSM}-columns netmonics-md-is-${ColumnsMD}-columns netmonics-lg-is-${ColumnsLG}-columns netmonics-xl-is-${ColumnsXL}-columns netmonics-xxl-is-${ColumnsXXL}-columns</code>;

    Please let me know if you have any other ideas and I’ll give them a try.

    Best Regards,

    Steve

    • This reply was modified 2 years, 5 months ago by Steve.
    Thread Starter Steve

    (@bugdens)

    It seems I can’t show ticks because this site uses them for displaying code.

    I put them where ‘xxx’ is in the following string:
    const myClasses = xxx<code>${backgroundColor.class} netmonics-xs-is-${ColumnsXS}-columns netmonics-sm-is-${ColumnsSM}-columns netmonics-md-is-${ColumnsMD}-columns netmonics-lg-is-${ColumnsLG}-columns netmonics-xl-is-${ColumnsXL}-columns netmonics-xxl-is-${ColumnsXXL}-columns</code>xxx;

    Thread Starter Steve

    (@bugdens)

    I’ve just noticed you modified your reply, sorry, I’ll take a look at that.

    Thread Starter Steve

    (@bugdens)

    I’m not sure because under the section titled ‘block wrapper props’ it says ‘classNames’ plural:

    The first thing to notice here is the use of the useBlockProps React hook on the block wrapper element. In the example above, the block wrapper renders a “div” in the editor, but in order for the Gutenberg editor to know how to manipulate the block, add any extra classNames . . .

    Though the example below does show just a single class.

    Steve

    The reason I was wondering about the type of the value passed in is because the error mentions it is looking for a length property, which suggests it is expecting an array:

    Cannot read properties of undefined (reading 'length')

    So I was wondering if it’s expecting either a string for a single classname or an array of multiple classnames. The source shows it’s using the classnames package which should accept a valid string or array: https://github.com/WordPress/gutenberg/blob/trunk/packages/block-editor/src/components/block-list/use-block-props/index.js#L154

    I think this will require some deeper investigation. Are you able to share the entire Edit.js file please?

    Thread Starter Steve

    (@bugdens)

    Hi Adam,

    Thank you so much for taking a look at this.

    Sure, I put the edit.js below.

    Steve

    import { useEffect } from '@wordpress/element';
    
    import {
    	RichText,
    	useBlockProps,
    	InnerBlocks,
    	InspectorControls,
    	PanelColorSettings,
    	//ContrastChecker,
    	withColors,
    } from '@wordpress/block-editor';
    
    import {
    	Panel,
    	PanelBody,
    	PanelRow,
    	RangeControl,
    	RadioControl,
    	ToggleControl,
    	TextControl,
    } from '@wordpress/components';
    
    import { __ } from '@wordpress/i18n';
    import './editor.scss';
    
    function Edit(props) {
    	const {
    		attributes,
    		setAttributes,
    		backgroundColor,
    		//textColor,
    		setBackgroundColor,
    		//setTextColor,
    	} = props;
    
    	const {
    		ColumnsXS,
    		ColumnsSM,
    		ColumnsMD,
    		ColumnsLG,
    		ColumnsXL,
    		ColumnsXXL,
    		mainTitle,
    		mainTitleAlignment,
    		EnableSocialMediaButtons,
    		DefaultButtonTextValue,
    		TitlePosition,
    		UniqueID,
    		LayoutStyle,
    		//BelowImageCardTitleClass,
    		//AboveImageCardTitleClass,
    	} = attributes;
    
    	//Generate a value for the UniqueID if not set
    
    	const onChangeID = (newID) => {
    		setAttributes({ UniqueID: newID });
    	};
    
    	const onChangeColumnsXS = (newColumns) => {
    		setAttributes({ ColumnsXS: newColumns });
    	};
    
    	const onChangeColumnsSM = (newColumns) => {
    		setAttributes({ ColumnsSM: newColumns });
    	};
    
    	const onChangeColumnsMD = (newColumns) => {
    		setAttributes({ ColumnsMD: newColumns });
    	};
    
    	const onChangeColumnsLG = (newColumns) => {
    		setAttributes({ ColumnsLG: newColumns });
    	};
    
    	const onChangeColumnsXL = (newColumns) => {
    		setAttributes({ ColumnsXL: newColumns });
    	};
    
    	const onChangeColumnsXXL = (newColumns) => {
    		setAttributes({ ColumnsXXL: newColumns });
    	};
    
    	const onChangeMainTitle = (value) => {
    		setAttributes({ mainTitle: value });
    	};
    
    	const onChangeTitleAlignment = (value) => {
    		setAttributes({ mainTitleAlignment: value });
    	};
    
    	const onChangeLayoutStyle = (value) => {
    		setAttributes({ LayoutStyle: value });
    	};
    
    	const onChangedDefaultButtonText = (value) => {
    		setAttributes({ DefaultButtonTextValue: value });
    	};
    
    	const onChangeTitlePosition = (value) => {
    		setAttributes({ TitlePosition: value });
    		//(value);
    	};
    
    	useEffect(() => {
    		if (TitlePosition === 'above-image') {
    			setAttributes({
    				AboveImageCardTitleClass: 'title-above-image-displayed',
    			});
    			setAttributes({
    				BelowImageCardTitleClass: 'title-below-image-not-displayed',
    			});
    		} else {
    			setAttributes({
    				AboveImageCardTitleClass: 'title-above-image-not-displayed',
    			});
    			setAttributes({
    				BelowImageCardTitleClass: 'title-below-image-displayed',
    			});
    		}
    	}, [TitlePosition]);
    
    	//useEffect(() => {
    	//return;
    	//document.getElementById {UniqueID}
    	//const SetTitlePosition = (value) => {
    	// 	if (TitlePosition === 'above-image') {
    	// 		//Display the title above the image
    
    	// 		const a = document.getElementsByClassName(
    	// 			<code>title-above-image-not-displayed ${UniqueID}</code>
    	// 		);
    
    	// 		[...a].forEach(
    	// 			(x) => (x.className += ' title-above-image-displayed ')
    	// 		);
    	// 		[...a].forEach((x) =>
    	// 			x.classList.remove('title-above-image-not-displayed')
    	// 		);
    
    	// 		//Hide the title below the image
    	// 		const b = document.getElementsByClassName(
    	// 			<code>title-below-image-displayed  ${UniqueID}</code>
    	// 		);
    	// 		[...b].forEach(
    	// 			(x) => (x.className += ' title-below-image-not-displayed')
    	// 		);
    	// 		[...b].forEach((x) =>
    	// 			x.classList.remove('title-below-image-displayed')
    	// 		);
    	// 	} else {
    	// 		//**************************************************************************************************************
    	// 		//Find every instance of the class which disables the display of the social media buttons and replace it with
    	// 		//the class to enable the display.
    	// 		const a = document.getElementsByClassName(
    	// 			<code>title-above-image-displayed ${UniqueID}</code>
    	// 		);
    	// 		[...a].forEach(
    	// 			(x) => (x.className += ' title-above-image-not-displayed')
    	// 		);
    	// 		[...a].forEach((x) =>
    	// 			x.classList.remove('title-above-image-displayed')
    	// 		);
    
    	// 		const b = document.getElementsByClassName(
    	// 			<code>title-below-image-not-displayed  ${UniqueID}</code>
    	// 		);
    	// 		[...b].forEach(
    	// 			(x) => (x.className += ' title-below-image-displayed')
    	// 		);
    	// 		[...b].forEach((x) =>
    	// 			x.classList.remove('title-below-image-not-displayed')
    	// 		);
    
    	// 		//**************************************************************************************************************
    	// 	}
    	// 	//};
    	// }, [TitlePosition, LayoutStyle]);
    
    	const onChangeEnableSocialMediaButtons = (value) => {
    		setAttributes({ EnableSocialMediaButtons: value });
    	};
    
    	//If the social media buttons are enabled, search through each card and display the social media buttons for each.
    	useEffect(() => {
    		if (EnableSocialMediaButtons === true) {
    			//Find every instance of the class which disables the display of the social media buttons and replace it with
    			//the class to enable the display.
    			const a = document.getElementsByClassName(
    				'social-media-links-disabled'
    			);
    			[...a].forEach(
    				(x) => (x.className += ' social-media-links-enabled')
    			);
    			[...a].forEach((x) =>
    				x.classList.remove('social-media-links-disabled')
    			);
    		} else {
    			//**************************************************************************************************************
    			//Find every instance of the class which disables the display of the social media buttons and replace it with
    			//the class to enable the display.
    			const a = document.getElementsByClassName(
    				'social-media-links-enabled'
    			);
    			[...a].forEach(
    				(x) => (x.className += ' social-media-links-disabled')
    			);
    			[...a].forEach((x) =>
    				x.classList.remove('social-media-links-enabled')
    			);
    			//**************************************************************************************************************
    		}
    	}, [EnableSocialMediaButtons]);
    
    	//heading alignment
    	const mainTitleClass = <code>main-heading-align-${mainTitleAlignment}</code>;
    
    	if (UniqueID === '') {
    		const lID =
    			Math.random().toString(36).substring(2, 15) +
    			Math.random().toString(36).substring(2, 15);
    		onChangeID(lID);
    		// console.log('just set it to = ' + lID);
    	}
    
    	// let divStyles = {};
    	// const className = getColorClassName(
    	// 	'background-color',
    
    	//);
    	//className={backgroundColor.class}
    	//console.log('props');
    	//console.log(props);
    	//const myClasses = <code><code>${backgroundColor.class} netmonics-xs-is-${ColumnsXS}-columns netmonics-sm-is-${ColumnsSM}-columns netmonics-md-is-${ColumnsMD}-columns netmonics-lg-is-${ColumnsLG}-columns netmonics-xl-is-${ColumnsXL}-columns netmonics-xxl-is-${ColumnsXXL}-columns</code></code>;
    
    	//console.log(myClasses);
    	const myClasses = <code>${backgroundColor.class} netmonics-xs-is-${ColumnsXS}-columns netmonics-sm-is-${ColumnsSM}-columns netmonics-md-is-${ColumnsMD}-columns netmonics-lg-is-${ColumnsLG}-columns netmonics-xl-is-${ColumnsXL}-columns netmonics-xxl-is-${ColumnsXXL}-columns</code>;
    	//console.log(myClasses);
    	//const myClasses =
    	//' netmonics-xs-is-2-columns netmonics-sm-is-3-columns netmonics-md-is-3-columns netmonics-lg-is-4-columns netmonics-xl-is-4-columns netmonics-xxl-is-4-columns ';
    
    	return (
    		<div
    			id="{UniqueID}"
    			{...useBlockProps({
    				className: myClasses,
    			})}
    		>
    			<InspectorControls>
    				<PanelColorSettings
    					title={__('Color Settings', 'text-box')}
    					icon="admin-appearance"
    					initialOpen={false}
    					disableCustomColors={false}
    					colorSettings={[
    						{
    							value: backgroundColor.color,
    							onChange: setBackgroundColor,
    							label: __('Background Color', 'text-box'),
    						},
    						// {
    						// 	value: textColor.color,
    						// 	onChange: setTextColor,
    						// 	label: __('Text Color', 'text-box'),
    						// },
    					]}
    				>
    					{/* <ContrastChecker
    						textColor={textColor.color}
    						backgroundColor={backgroundColor.color}
    					/> */}
    				</PanelColorSettings>
    
    				<Panel header="">
    					<PanelBody title="Columns" initialOpen={false}>
    						<PanelRow>
    							{__(
    								'Set the number of columns for each screen size',
    								'team-members'
    							)}
    						</PanelRow>
    						<PanelRow>{__('', 'team-members')}</PanelRow>
    						<RangeControl
    							label={__(
    								'Extra Small (less than 575px)',
    								'team-members'
    							)}
    							min={1}
    							max={6}
    							onChange={onChangeColumnsXS}
    							value={ColumnsXS}
    						/>
    						<RangeControl
    							label={__(
    								'Small (between 575px and 768px)',
    								'team-members'
    							)}
    							min={1}
    							max={6}
    							onChange={onChangeColumnsSM}
    							value={ColumnsSM}
    						/>
    						<RangeControl
    							label={__(
    								'Medium (between 768px and 992px)',
    								'team-members'
    							)}
    							min={1}
    							max={6}
    							onChange={onChangeColumnsMD}
    							value={ColumnsMD}
    						/>
    						<RangeControl
    							label={__(
    								'Large (between 992px and 1200px)',
    								'team-members'
    							)}
    							min={1}
    							max={6}
    							onChange={onChangeColumnsLG}
    							value={ColumnsLG}
    						/>
    						<RangeControl
    							label={__(
    								'XL (between 1200px and 1400px)',
    								'team-members'
    							)}
    							min={1}
    							max={6}
    							onChange={onChangeColumnsXL}
    							value={ColumnsXL}
    						/>
    						<RangeControl
    							label={__(
    								'XXL (greater than 1400px)',
    								'team-members'
    							)}
    							min={1}
    							max={6}
    							onChange={onChangeColumnsXXL}
    							value={ColumnsXXL}
    						/>
    					</PanelBody>
    				</Panel>
    
    				<Panel header="">
    					<PanelBody
    						title="Call to Action Button "
    						initialOpen={false}
    					>
    						<TextControl
    							label="Default Text"
    							value={DefaultButtonTextValue}
    							onChange={onChangedDefaultButtonText}
    							help={__('Enter the default text for the button')}
    						/>
    					</PanelBody>
    				</Panel>
    
    				<Panel header="">
    					<PanelBody title="Main Heading" initialOpen={false}>
    						<PanelRow>
    							{__('Configure the heading', 'team-members')}
    						</PanelRow>
    						<PanelRow>
    							{__('', 'team-members')}
    
    							<RadioControl
    								label="Alignment"
    								selected={mainTitleAlignment}
    								options={[
    									{ label: 'Left', value: 'left' },
    									{ label: 'Centre', value: 'center' },
    								]}
    								onChange={onChangeTitleAlignment}
    							/>
    						</PanelRow>
    					</PanelBody>
    				</Panel>
    
    				<Panel header="">
    					<PanelBody title="Layout Style" initialOpen={false}>
    						<PanelRow>
    							{__('Configure the layout', 'team-members')}
    						</PanelRow>
    						<PanelRow>
    							{__('', 'team-members')}
    
    							<RadioControl
    								label="Layout"
    								selected={LayoutStyle}
    								options={[
    									{ label: 'Stacked', value: 'stacked' },
    									{
    										label: 'Image Left',
    										value: 'image-left',
    									},
    								]}
    								onChange={onChangeLayoutStyle}
    							/>
    						</PanelRow>
    					</PanelBody>
    				</Panel>
    
    				<Panel header="">
    					<PanelBody title="Social Media Buttons" initialOpen={false}>
    						<PanelRow>{__('', 'team-members')}</PanelRow>
    
    						<PanelRow>
    							<ToggleControl
    								label="Enable Social Media Buttons"
    								checked={EnableSocialMediaButtons}
    								onChange={onChangeEnableSocialMediaButtons}
    							/>
    						</PanelRow>
    					</PanelBody>
    				</Panel>
    
    				{/* It only makes sense to display the title position options if the layout style is stacked */}
    				{LayoutStyle === 'stacked' && (
    					<Panel header="">
    						<PanelBody title="Title" initialOpen={false}>
    							<PanelRow>{__('', 'team-members')}</PanelRow>
    
    							<PanelRow>
    								<RadioControl
    									label="Set title position above or below the image"
    									selected={TitlePosition}
    									options={[
    										{
    											label: 'Above',
    											value: 'above-image',
    										},
    										{
    											label: 'Below',
    											value: 'below-image',
    										},
    									]}
    									onChange={onChangeTitlePosition}
    								/>
    							</PanelRow>
    						</PanelBody>
    					</Panel>
    				)}
    			</InspectorControls>
    
    			<RichText
    				placeholder={__('UniqueID', 'team-member')}
    				tagName="p"
    				onChange={onChangeID}
    				value={UniqueID}
    				allowedFormats={[]}
    				className="netmonics-cards-unique-id"
    			/>
    
    			<RichText
    				placeholder={__('Main Title', 'team-members')}
    				tagName="h2"
    				value={mainTitle}
    				allowedFormats={[]}
    				onChange={onChangeMainTitle}
    				className={mainTitleClass}
    			/>
    
    			<InnerBlocks
    				allowedBlocks={['netmonics/generic-card']}
    				orientation="horizontal" // Set the orientation to horizontal as the blocks are aligned horizontally and when dragged and dropped
    				// this will cause a blue vertical line to appear indicating where the block will be dropped.
    				template={[
    					['netmonics/generic-card'],
    					['netmonics/generic-card'],
    					['netmonics/generic-card'],
    				]}
    				//templateLock="all"  //Prevent more items being added, 'insert' is also a valid option.
    			/>
    		</div>
    	);
    }
    
    export default withColors({
    	backgroundColor: 'background-color',
    	// textColor: 'color',
    })(Edit);
Viewing 7 replies - 1 through 7 (of 7 total)
  • The topic ‘useBlockProps seems to cause an error’ is closed to new replies.