‘useSate’ error when using React on the frontend in custom block plugin

I tried your view script, i.e. the code in your view.js file, and it’s true that I also got the same error in question. However, I spotted another error which appeared before the other error, and which explained why that other error appeared:

Warning: Invalid hook call. Hooks can only be called inside of the
body of a function component.

  1. You might have mismatching versions of React and the renderer (such as React DOM)
  2. You might be breaking the Rules of Hooks
  3. You might have more than one copy of React in the same app

See https://reactjs.org/link/invalid-hook-call for tips about how to
debug and fix this problem.

So in your case, the problem is that you’re breaking the Rules of Hooks, i.e. you’re calling useState (which is a React hook) from within an event handler (which is the function called once the DOM is ready).

Therefore, instead of doing this:

window.addEventListener( 'DOMContentLoaded', ( event ) => {

    const wrapper = document.getElementById( `nav-block` );
    // You can not call useState here.
    const [isOpen, setOpen] = useState(false);

    render( <Hamburger toggled={isOpen} toggle={setOpen} />, wrapper );

} );

You can try something like this:

const MyHamburger = () => {
    // This is within a *function* component, so useState can be used.
    const [isOpen, setOpen] = useState(false);

    return <Hamburger toggled={ isOpen } toggle={ setOpen } />;
};

window.addEventListener( 'DOMContentLoaded', ( event ) => {

    const wrapper = document.getElementById( `nav-block` );

    render( <MyHamburger />, wrapper );

} );

Additional Notes

  1. I had script debugging enabled, i.e. SCRIPT_DEBUG is true, hence you should do the same while developing/testing your block, to ensure your code is free from errors/warnings/etc. 🙂

  2. I used WordPress v6.2, hence I also got this warning when I tested your code: (see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-element/#usage)

    Warning: ReactDOM.render is no longer supported in React 18. Use
    createRoot instead. Until you switch to the new API, your app will
    behave as if it’s running React 17. Learn more:
    https://reactjs.org/link/switch-to-createroot

    So this is how I got rid of that warning:

    // At the top in the file, I imported createRoot instead:
    import { createRoot, useState } from '@wordpress/element';
    
    // ... then called it instead of wp.element.render():
    window.addEventListener( 'DOMContentLoaded', ( event ) => {
    
        const wrapper = document.getElementById( 'nav-block' );
    
        createRoot( wrapper ).render( <MyHamburger /> );
    
    } );
    

    However, you could also import both createRoot and render, and then replace the createRoot line above with:

    if ( createRoot ) {
        createRoot( wrapper ).render( <MyHamburger /> );
    } else {
        render( <MyHamburger />, wrapper );
    }