Wordpress-Gutenberg/React: MediaUpload?

I’m trying to make a Gutenberg block that will let me select/upload multiple images from the WordPress Media Manager. I’m totally new to React and building Gutenberg blocks.

Ideally what I’d like to have is a button in the Inspector Controls that opens the Media Manager, and then save the IDs of the selected media to an object/array in block $attributes.

I’ve tried over a dozen different combinations to try to get the to work, but it breaks the block every time. The console log has a very generic “not defined” error.

Where’s what I have so far for my edit.js

/**
 * Retrieves the translation of text.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-i18n/
 */
import { __ } from '@wordpress/i18n';
import {DynamicGallery, StaticGallery,MyMediaUploader} from './components/gallery';
import metadata from './block.json';
import "./editor.scss"
/**
 * 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, InspectorControls } from '@wordpress/block-editor';
import { useState } from 'react';
import {
  PanelBody,
  MediaUploadCheck,
  MediaUpload,
  SelectControl,
  __experimentalNumberControl as NumberControl,
  Button,
  ToggleControl,
  DropZone,
  TextControl
} from '@wordpress/components';

/**
 * 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 {Element} Element to render.
 */
export default function Edit(props) {
  console.log(props);
  const{className, ...blockProps} = useBlockProps();
  
  return (
    <>

      

      <InspectorControls>
        <PanelBody title={__('Source Settings')}>


        <SelectControl
            label={__('Images Source', metadata.textdomain)}
            value={props.attributes.source}
            onChange={(newValue) => props.setAttributes({ source: newValue })}
            options={[
              
              { value: 'manual', label: __('Manually Select', metadata.textdomain) },
              { value: 'meta_field', label: __('Meta field of current post', metadata.textdomain)  },
              { value: 'linked_table', label: __('Linked Table', metadata.textdomain)  }

            ]}
         />
       

          {props.attributes.source == "meta_field" && (
            <div style={{ display: 'flex', alignItems: 'center' }}>
              <TextControl
                  label="Meta Field Name"
                  value={props.attributes.sourceMetaField}
                  onChange={(newValue) => props.setAttributes({ sourceMetaField: newValue })}
                />
            </div>
          )}
          {props.attributes.source == "linked_table" && (
            <div style={{ display: 'flex', alignItems: 'center' }}>
              <TextControl
                  label="Table Name"
                  value={props.attributes.sourceTable}
                  onChange={(newValue) => props.setAttributes({ sourceTable: newValue })}
                />
            </div>
          )}
                    {props.attributes.source == "linked_table" && (
            <div style={{ display: 'flex', alignItems: 'center' }}>
              <TextControl
                  label="Column Name"
                  value={props.attributes.sourceColumn}
                  onChange={(newValue) => props.setAttributes({ sourceColumn: newValue })}
                />
            </div>
          )}
          <div style={{ display: 'flex', alignItems: 'center' }}>
            <ToggleControl onChange={(isChecked) => {
              props.setAttributes({
                includePostThumbnail: isChecked
              })
            }
            } checked={props.attributes.includePostThumbnail}
              label={__('Include Post Thumbnail (Featured Image)', metadata.textdomain)}
            />
          </div>
    
          <div style={{ display: 'block' }}>
            <Button isPrimary>{__('Select Media')}</Button>
            <DropZone />
          </div>
        </PanelBody>
        <PanelBody title={__('Display Settings')}>
       
        <SelectControl
            label={__('Select an Option', metadata.textdomain)}
            value={props.attributes.initialLayout}
            onChange={(newValue) => props.setAttributes({ initialLayout: newValue })}
            options={[
              
              { value: 'inline', label: __('Inline', metadata.textdomain) },
              { value: 'grid', label: __('Grid', metadata.textdomain)  },
              { value: 'justified', label: __('Justified', metadata.textdomain)  },
              { value: 'masonry', label: __('Masonry', metadata.textdomain) }

            ]}
         />


            {props.attributes.initialLayout == "grid" && (
                  <div style={{ display: 'block' }}>
                  <span>{__('Grid Columns', metadata.textdomain)}</span>
                  <NumberControl
                    onChange={(newValue) => {
                      props.setAttributes({
                        gridColumns: parseInt(newValue)
                      });
                    }}
                    value={props.attributes.gridColumns}
                    isShiftStepEnabled={true}
                    shiftStep={10}
                  />
                </div>
              )}
        
          <div style={{ display: 'block', alignItems: 'center' }}>
            <ToggleControl
              onChange={(isChecked) => {
                props.setAttributes({
                  dynamic: isChecked
                })
              }
              } checked={props.attributes.dynamic}
              label={__('Load Dynamically', metadata.textdomain)}
            />
          </div>
          {props.attributes.dynamic && (
            <div style={{ display: 'block', alignItems: 'center' }}>
              <span>{__('Max to load', metadata.textdomain)}</span>
              <NumberControl
                onChange={(newValue) => {
                  props.setAttributes({
                    maxInitialImages: parseInt(newValue)
                  });
                }}
                value={props.attributes.maxInitialImages}
                isShiftStepEnabled={true}
                shiftStep={10}
              />
            </div>
            )}


        </PanelBody>


        <PanelBody title={__('Gallery Options')}>
        <div style={{ display: 'flex', alignItems: 'center' }}>
            <ToggleControl
              onChange={(isChecked) => {
                props.setAttributes({
                  loop: isChecked
                })
              }
              } checked={props.attributes.loop}
              label={__('Enable Loop', metadata.textdomain)}
            />
          </div>
          <div style={{ display: 'flex', alignItems: 'center' }}>
            <ToggleControl
                onChange={(isChecked) => {
                props.setAttributes({
                  download: isChecked
                })
              }
              } checked={props.attributes.download}
              label={__('Allow Download', metadata.textdomain)}
            />
          </div>
          {props.attributes.download  && (
          <SelectControl
            label={__('Download Image Size', metadata.textdomain)}
            value={props.attributes.downloadMaxSize}
            onChange={(newValue) => props.setAttributes({ downloadMaxSize: newValue })}
            options={[
              
              { value: 'full', label: __('Full (Original Size)', metadata.textdomain) },
              { value: 'medium', label: __('Medium (Up to 300x300px - Not Cropped)', metadata.textdomain)  },
              { value: 'large', label: __('Large (Up to 1024 x 1024px - Not Cropped)', metadata.textdomain)  },
              { value: 'thumbnail', label: __('Thumbnail (150 x 150px - Cropped', metadata.textdomain) }

            ]}
         />
          )}

          <div style={{ display: 'flex', alignItems: 'center' }}>
            <ToggleControl
                  onChange={(isChecked) => {
                  props.setAttributes({
                    allowZoom: isChecked
                  })
                }
                } checked={props.attributes.allowZoom}
              label={__('Allow Zoom In/Out', metadata.textdomain)}
            />
          </div>
          <div style={{ display: 'flex', alignItems: 'center' }}>
            <ToggleControl
                onChange={(isChecked) => {
                props.setAttributes({
                  closeOnTap: isChecked
                })
              }
              } checked={props.attributes.closeOnTap}
              label={__('Close on tap', metadata.textdomain)}
            />
          </div>
          <div style={{ display: 'flex', alignItems: 'center' }}>
            <ToggleControl
                onChange={(isChecked) => {
                props.setAttributes({
                  showCounter: isChecked
                })
              }
              } checked={props.attributes.showCounter}
              label={__('Show counter', metadata.textdomain)}
            />
          </div>
          <div style={{ display: 'flex', alignItems: 'center' }}>
            <ToggleControl
              onChange={(isChecked) => {
                props.setAttributes({
                  showControls: isChecked
                })
              }
              } checked={props.attributes.showControls}
              label={__('Show Previous/Next Buttons', metadata.textdomain)}
            />
          </div>
        </PanelBody>
 
      </InspectorControls>
    </>
  );
}
/**
 * Retrieves the translation of text.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-i18n/
 */
import { __ } from '@wordpress/i18n';
import {DynamicGallery, StaticGallery,MyMediaUploader} from './components/gallery';
import metadata from './block.json';
import "./editor.scss"
/**
 * 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, InspectorControls } from '@wordpress/block-editor';
import { useState } from 'react';
import {
  PanelBody,
  MediaUploadCheck,
  MediaUpload,
  SelectControl,
  __experimentalNumberControl as NumberControl,
  Button,
  ToggleControl,
  DropZone,
  TextControl
} from '@wordpress/components';

/**
 * 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 {Element} Element to render.
 */
export default function Edit(props) {
  console.log(props);
  const{className, ...blockProps} = useBlockProps();
  
  return (
    <>

      

      <InspectorControls>
        <PanelBody title={__('Source Settings')}>


        <SelectControl
            label={__('Images Source', metadata.textdomain)}
            value={props.attributes.source}
            onChange={(newValue) => props.setAttributes({ source: newValue })}
            options={[
              
              { value: 'manual', label: __('Manually Select', metadata.textdomain) },
              { value: 'meta_field', label: __('Meta field of current post', metadata.textdomain)  },
              { value: 'linked_table', label: __('Linked Table', metadata.textdomain)  }

            ]}
         />
       

          {props.attributes.source == "meta_field" && (
            <div style={{ display: 'flex', alignItems: 'center' }}>
              <TextControl
                  label="Meta Field Name"
                  value={props.attributes.sourceMetaField}
                  onChange={(newValue) => props.setAttributes({ sourceMetaField: newValue })}
                />
            </div>
          )}
          {props.attributes.source == "linked_table" && (
            <div style={{ display: 'flex', alignItems: 'center' }}>
              <TextControl
                  label="Table Name"
                  value={props.attributes.sourceTable}
                  onChange={(newValue) => props.setAttributes({ sourceTable: newValue })}
                />
            </div>
          )}
                    {props.attributes.source == "linked_table" && (
            <div style={{ display: 'flex', alignItems: 'center' }}>
              <TextControl
                  label="Column Name"
                  value={props.attributes.sourceColumn}
                  onChange={(newValue) => props.setAttributes({ sourceColumn: newValue })}
                />
            </div>
          )}
          <div style={{ display: 'flex', alignItems: 'center' }}>
            <ToggleControl onChange={(isChecked) => {
              props.setAttributes({
                includePostThumbnail: isChecked
              })
            }
            } checked={props.attributes.includePostThumbnail}
              label={__('Include Post Thumbnail (Featured Image)', metadata.textdomain)}
            />
          </div>
    
          <div style={{ display: 'block' }}>
            <Button isPrimary>{__('Select Media')}</Button>
            <DropZone />
          </div>
        </PanelBody>
        <PanelBody title={__('Display Settings')}>
       
        <SelectControl
            label={__('Select an Option', metadata.textdomain)}
            value={props.attributes.initialLayout}
            onChange={(newValue) => props.setAttributes({ initialLayout: newValue })}
            options={[
              
              { value: 'inline', label: __('Inline', metadata.textdomain) },
              { value: 'grid', label: __('Grid', metadata.textdomain)  },
              { value: 'justified', label: __('Justified', metadata.textdomain)  },
              { value: 'masonry', label: __('Masonry', metadata.textdomain) }

            ]}
         />


            {props.attributes.initialLayout == "grid" && (
                  <div style={{ display: 'block' }}>
                  <span>{__('Grid Columns', metadata.textdomain)}</span>
                  <NumberControl
                    onChange={(newValue) => {
                      props.setAttributes({
                        gridColumns: parseInt(newValue)
                      });
                    }}
                    value={props.attributes.gridColumns}
                    isShiftStepEnabled={true}
                    shiftStep={10}
                  />
                </div>
              )}
        
          <div style={{ display: 'block', alignItems: 'center' }}>
            <ToggleControl
              onChange={(isChecked) => {
                props.setAttributes({
                  dynamic: isChecked
                })
              }
              } checked={props.attributes.dynamic}
              label={__('Load Dynamically', metadata.textdomain)}
            />
          </div>
          {props.attributes.dynamic && (
            <div style={{ display: 'block', alignItems: 'center' }}>
              <span>{__('Max to load', metadata.textdomain)}</span>
              <NumberControl
                onChange={(newValue) => {
                  props.setAttributes({
                    maxInitialImages: parseInt(newValue)
                  });
                }}
                value={props.attributes.maxInitialImages}
                isShiftStepEnabled={true}
                shiftStep={10}
              />
            </div>
            )}


        </PanelBody>


        <PanelBody title={__('Gallery Options')}>
        <div style={{ display: 'flex', alignItems: 'center' }}>
            <ToggleControl
              onChange={(isChecked) => {
                props.setAttributes({
                  loop: isChecked
                })
              }
              } checked={props.attributes.loop}
              label={__('Enable Loop', metadata.textdomain)}
            />
          </div>
          <div style={{ display: 'flex', alignItems: 'center' }}>
            <ToggleControl
                onChange={(isChecked) => {
                props.setAttributes({
                  download: isChecked
                })
              }
              } checked={props.attributes.download}
              label={__('Allow Download', metadata.textdomain)}
            />
          </div>
          {props.attributes.download  && (
          <SelectControl
            label={__('Download Image Size', metadata.textdomain)}
            value={props.attributes.downloadMaxSize}
            onChange={(newValue) => props.setAttributes({ downloadMaxSize: newValue })}
            options={[
              
              { value: 'full', label: __('Full (Original Size)', metadata.textdomain) },
              { value: 'medium', label: __('Medium (Up to 300x300px - Not Cropped)', metadata.textdomain)  },
              { value: 'large', label: __('Large (Up to 1024 x 1024px - Not Cropped)', metadata.textdomain)  },
              { value: 'thumbnail', label: __('Thumbnail (150 x 150px - Cropped', metadata.textdomain) }

            ]}
         />
          )}

          <div style={{ display: 'flex', alignItems: 'center' }}>
            <ToggleControl
                  onChange={(isChecked) => {
                  props.setAttributes({
                    allowZoom: isChecked
                  })
                }
                } checked={props.attributes.allowZoom}
              label={__('Allow Zoom In/Out', metadata.textdomain)}
            />
          </div>
          <div style={{ display: 'flex', alignItems: 'center' }}>
            <ToggleControl
                onChange={(isChecked) => {
                props.setAttributes({
                  closeOnTap: isChecked
                })
              }
              } checked={props.attributes.closeOnTap}
              label={__('Close on tap', metadata.textdomain)}
            />
          </div>
          <div style={{ display: 'flex', alignItems: 'center' }}>
            <ToggleControl
                onChange={(isChecked) => {
                props.setAttributes({
                  showCounter: isChecked
                })
              }
              } checked={props.attributes.showCounter}
              label={__('Show counter', metadata.textdomain)}
            />
          </div>
          <div style={{ display: 'flex', alignItems: 'center' }}>
            <ToggleControl
              onChange={(isChecked) => {
                props.setAttributes({
                  showControls: isChecked
                })
              }
              } checked={props.attributes.showControls}
              label={__('Show Previous/Next Buttons', metadata.textdomain)}
            />
          </div>
        </PanelBody>
 
      </InspectorControls>
    </>
  );
}

Well how about specifically telling us what that error is pointing at/saying. Line number. Variable name. Yaknow, the things that would help, rather than just “here’s my file, something goes wrong” :stuck_out_tongue: