How to hot reload without being redirected to root with electron-forge and react

Viewed 1728

I'm having a problem with electron-forge, which I imagine is a common problem, but I can't seem to find the answer.

I have created a new project with npx create-electron-app and I have installed and setup react, and it's all working just fine.

The problem I have is with the hot reload. I am using react-router-dom for routing, and I've used the MemoryRouter as obviously, I don't have URL's running an electron app. The hot reload is working out of the box, but each time it reloads I get navigated back to the entry point of the app, and I would like to stay at on the current page. The annoying thing is, I can usually see the current page update for a split second before it navigates my back to root. Is there a setting somewhere to prevent this?

Appreciate any help.

2 Answers

Two things that would certainly cause this,

<Switch>
   <Route path="/" component={RootComponent} />
   <Route path="/test" component={TestComponent} />
</Switch>
  1. Check your routes, do you have the base route without exact keyword?

The above will match all routes to the root route and will land you in the root component. You need to add exact key

<Route exact path="/" component={RootComponent} />
  1. Do you have homepage: "." in package.json or <base href="/"> set in index.html? If yes, remove them.

Homepage: '.' will default to serving from the root.

What are the props you have used in the MemoryRouter? Like the initialIndex, initialEntries etc?

Did you try Hot module replacement? I'm not suggesting this as a fix for this issue but it's a good workaround Electron-forge doc for HMR

 entryPoints: [{
          rhmr: 'react-hot-loader/patch', // react hot module replacement
          name: 'main_window',
          html: './src/renderer/index.html',
          js: './src/renderer/index.js'
        }]

Electron-forge doc says it's not possible to do HMR inside the renderer, can you try the above anyways? It worked for me in the webpack app.

Please provide a minimal reproducible code repo/codesandbox to help you with the fix if the above didn't help. Most importantly, need to see the MemoryRouter usage in your app.

Update in response to the comment:

Yes, electron forge does hot reload by default, only changes to the CSS files are hot reloaded (without refresh), changes to JS files will need a refresh, in both cases files are being watched for changes. You can see note in the webpack-dev-server config (snapshot below)

enter image description here

Looking at your gist on MemoryRouter, I suggest below changes

  1. Provide the memory history prop to the router.

        import {createMemoryHistory} from 'history';
    
        const history = createMemoryHistory();
    
        <Router history={history}>
          <Switch>
            <Route path='/dash' exact component={Dash} />
            <Route path='/wallet' exact component={Wallet} />
            <Route path='/service' exact component={ServiceStatus} />
            <Route path='/' exact component={SignIn} />
            <Route component={SignIn} /> // would suggest protected routes 
          </Switch>
        </Router>
    
  2. Remove exact from other routes than the base route and place base above sign-in.

If the issue persists, I suggest you enable the hot module replacement (which would update your app without reload)

Here is an elaborate writeup on Hot Module Replacement applauded by the co-creator of redux and create-react-app.

A gif on HMR taken from above post (No refresh, so no landing on root route on every change yet the app is updated):

enter image description here

An example app to see it in action.

Electron forge HMR solution

Electron forge react example project

You can use electron-reloader to achieve this during development. If changes is made on the main process, the main process gets updated. If changes is made to the renderer process, the renderer process gets updated https://www.npmjs.com/package/electron-reloader

Another approach is to log the current route on the render process and save it on the localstorage. You can then go to that route when the page reloads. If you want to keep the state, you might need to save the state when you log the route.

localStorage.setItem('current_route', route)
localStorage.setItem('current_state, the_entire_state)

You can use the necessary lifecycle, to go to the required route and set the state

Related