scratchfoundation / scratch-gui

Graphical User Interface for creating and running Scratch 3.0 projects.
https://scratchfoundation.github.io/scratch-gui/develop/
BSD 3-Clause "New" or "Revised" License
4.47k stars 3.57k forks source link

Is it possible to use react-router-dom in scratch-gui? If so, how can I do it? #9707

Open aquino-luane opened 2 weeks ago

aquino-luane commented 2 weeks ago

I am trying to integrate react-router-dom into the scratch-gui mod project to handle routing. I attempted to configure the router in the app-state-hoc.jsx file, but it did not work as expected. Here is the code I tried:

import {
    BrowserRouter as Router,
    Switch,
    Route
} from 'react-router-dom';
....
render () {
            const {
                isFullScreen, // eslint-disable-line no-unused-vars
                isPlayerOnly, // eslint-disable-line no-unused-vars
                isTelemetryEnabled, // eslint-disable-line no-unused-vars
                showTelemetryModal, // eslint-disable-line no-unused-vars
                ...componentProps
            } = this.props;
            return (
                <Provider store={this.store}>
                    <ConnectedIntlProvider>
                        <Router>
                            <Switch>
                                {/* Use variable studioPath to support /studio-playground/ or future route */}
                                <Route path="/">
                                    <WrappedComponent
                                        {...componentProps}
                                    />
                                </Route>
                            </Switch>
                        </Router>
                    </ConnectedIntlProvider>
                </Provider>
            );
        }
...

When I run the application, I get the following warning:

app-state-hoc.jsx:110 Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

Any help or guidance would be greatly appreciated.

Tom-cy commented 1 week ago

render-gui.jsx

import React from 'react'; import ReactDOM from 'react-dom'; import { compose } from 'redux'; import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

import AppStateHOC from '../lib/app-state-hoc.jsx'; import GUI from '../containers/gui.jsx'; import HashParserHOC from '../lib/hash-parser-hoc.jsx'; import log from '../lib/log.js';

const onClickLogo = () => { window.location = '#'; };

const handleTelemetryModalCancel = () => { log('User canceled telemetry modal'); };

const handleTelemetryModalOptIn = () => { log('User opted into telemetry'); };

const handleTelemetryModalOptOut = () => { log('User opted out of telemetry'); };

export default (appTarget) => { GUI.setAppElement(appTarget);

const WrappedGui = compose(AppStateHOC, HashParserHOC)(GUI);

const backpackHostMatches = window.location.href.match( /[?&]backpack_host=([^&]*)&?/ ); const backpackHost = backpackHostMatches ? backpackHostMatches[1] : null;

const scratchDesktopMatches = window.location.href.match( /[?&]isScratchDesktop=([^&]+)/ ); let simulateScratchDesktop; if (scratchDesktopMatches) { try { simulateScratchDesktop = JSON.parse(scratchDesktopMatches[1]); } catch { simulateScratchDesktop = scratchDesktopMatches[1]; } }

if (process.env.NODE_ENV === 'production' && typeof window === 'object') { window.onbeforeunload = () => true; }

ReactDOM.render(

{simulateScratchDesktop ? ( ) : ( )} , appTarget ); }; 我昨天做的。
Tom-cy commented 1 week ago

and u can use "react-router-dom": "^5.3.4",

aquino-luane commented 1 week ago

render-gui.jsx

import React from 'react'; import ReactDOM from 'react-dom'; import { compose } from 'redux'; import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

import AppStateHOC from '../lib/app-state-hoc.jsx'; import GUI from '../containers/gui.jsx'; import HashParserHOC from '../lib/hash-parser-hoc.jsx'; import log from '../lib/log.js';

const onClickLogo = () => { window.location = '#'; };

const handleTelemetryModalCancel = () => { log('User canceled telemetry modal'); };

const handleTelemetryModalOptIn = () => { log('User opted into telemetry'); };

const handleTelemetryModalOptOut = () => { log('User opted out of telemetry'); };

export default (appTarget) => { GUI.setAppElement(appTarget);

const WrappedGui = compose(AppStateHOC, HashParserHOC)(GUI);

const backpackHostMatches = window.location.href.match( /[?&]backpack_host=([^&]*)&?/ ); const backpackHost = backpackHostMatches ? backpackHostMatches[1] : null;

const scratchDesktopMatches = window.location.href.match( /[?&]isScratchDesktop=([^&]+)/ ); let simulateScratchDesktop; if (scratchDesktopMatches) { try { simulateScratchDesktop = JSON.parse(scratchDesktopMatches[1]); } catch { simulateScratchDesktop = scratchDesktopMatches[1]; } }

if (process.env.NODE_ENV === 'production' && typeof window === 'object') { window.onbeforeunload = () => true; }

ReactDOM.render( {simulateScratchDesktop ? ( ) : ( )} , appTarget ); };

我昨天做的。

Hi @Tom-cy thanks for your answer! I see you are importing the Router, but where are you using it? 🤔

Tom-cy commented 1 week ago

emmmmm, this ?

<Router basename="/scratch3">
    <Switch>
        {/* 默认路由 */}
        <Route exact path="/">
            {simulateScratchDesktop ? (
                <WrappedGui
                    canEditTitle
                    isScratchDesktop
                    showTelemetryModal
                    canSave={false}
                    onTelemetryModalCancel={handleTelemetryModalCancel}
                    onTelemetryModalOptIn={handleTelemetryModalOptIn}
                    onTelemetryModalOptOut={handleTelemetryModalOptOut}
                />
            ) : (
                <WrappedGui
                    canEditTitle
                    backpackVisible
                    showComingSoon
                    backpackHost={backpackHost}
                    canSave={false}
                    onClickLogo={onClickLogo}
                />
            )}
        </Route>

        <Route path="/editor">
            <EditorComponent />
        </Route>

        <Route path="/profile">
            <ProfileComponent />
        </Route>

        <Route path="/project/:projectId">
            <ProjectComponent />
        </Route>

        <Route path="*">
            <NotFoundComponent />
        </Route>
    </Switch>
</Router>
aquino-luane commented 1 week ago

@Tom-cy thank you! it helped a lot, I was configuring the route in the wrong place instead of render-gui.jsx. I also need to show projectId in the address bar like you /project/:projectId. What is the ProjectComponent, what it shows? could you share what is in the ProjectComponent?

aquino-luane commented 1 week ago

@Tom-cy sorry but I have one more question 🙇‍♀️ After you setup router dom, does the icons failed to load? if so, how you fixed it? image