0

I tried building my own custom gallery gutenberg block as shown here to get more familiar with the block structure. Putting all the code into a single file works fine, but when I try to split it up in edit.js and save.js, it returns this error upon trying to add images to it: "Uncaught TypeError: setAttributes is not a function"

I wasn't able to resolve this, what's the issue?

attributes in block.json

"attributes": {
        "images" : {
            "type": "array" 
        }
},

edit.js

/**
 * Retrieves the translation of text.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-i18n/
 */
import { __ } from '@wordpress/i18n';

/**
 * React hook that is used to mark the block wrapper element.
 * It provides all the necessary props like the class name.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops
 */
import { 
    useBlockProps,
    MediaUpload
 } from '@wordpress/block-editor';

import {
    Button
} from '@wordpress/components';

//import {registerBlockType} from "@wordpress/blocks";
/**
 * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
 * Those files can contain any CSS code that gets applied to the editor.
 *
 * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
 */
import './editor.scss';



/**
 * The edit function describes the structure of your block in the context of the
 * editor. This represents what the editor will render when the block is used.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit
 *
 * @return {WPElement} Element to render.
 */
export default function Edit(attributes, className, setAttributes) {

    const blockProps = useBlockProps();

    // Getting the images of the array
    const {images = [] } = attributes;

    // to remove image from gallery
    const removeImage = (removeImage) => {
        //filter images 
        const newImages = images.filter( (image) => {
            // if current image is equal to removeImage, image will be returend
            if(image.id != removeImage.id) {
                return image;
            }
        });
        //save new state
        setAttributes({
            images: newImages,
        })
    }

    //Display the images
    const displayImages = (images) => {
        return (
            //Loop through images 
            images.map((image) => {
                return (
                    <div className="gallery-item-container">
                        <img className='gallery-item' src={image.url} key={ images.id } />
                        <div className='remove-item' onClick={() => removeImage(image)}><span class="dashicons dashicons-trash"></span></div>
                        <div className='caption-text'>{image.caption[0]}</div>
                    </div>
                )
            })
        )
    }
    
    //JSX to return
    return (
        <>
        <div {...blockProps}>
            <div className='gallery-grid'>
                {displayImages(images)}
            </div>

            <br/>

            <MediaUpload 
                onSelect={(media) => setAttributes({
                    images: [...images,...media],
                })
                    
                }
                type="image"
                multiple={true}
                gallery={true}
                value={images}
                render={({open}) => (
                    <Button className="select-images-button is-button is-default is-large" onClick={open}>
                        Add images
                    </Button>
                )}
            />
        </div>
        </>
    );
}
4
  • are you sure you want to use (attributes, className, setAttributes) for your function arguments? This is very weird, and not what the examples show, I've never seen an example that didn't use {} to destructure the props
    – Tom J Nowell
    Commented Feb 24, 2023 at 18:36
  • also note that using array type block attributes like this will make things difficult, it's much much easier to use a child block for each item in a gallery, and you get controls for reordering items and removing/duplicating them for free. It also means your items show up in the outliner controls, and you avoid a large number of easy to make mistakes and difficult problems. Making block variants and block styles of the default gallery is even easier though
    – Tom J Nowell
    Commented Feb 24, 2023 at 18:53
  • Oh my god, I feel so stupid now, somehow I didn't see that... Thank you! @TomJNowell And that's an interesting point with the child blocks, I will look into that!
    – Fou Rorty
    Commented Feb 25, 2023 at 10:01
  • I spent 2 days trying to debug a useState call before realising I'd used {} instead of [] it happens to the best of us
    – Tom J Nowell
    Commented Feb 25, 2023 at 15:16

1 Answer 1

0

Have you tried:

export default function Edit( { attributes, className, setAttributes } ) {

Edit is a react component, and react components receive props, so Edit( props ) is more accurate, hence the {} in the examples

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.