I'm on a Mac, but Electron is cross platform with Windows and Linux so it should work there too. ReactXP 1.6.x is current right now (hilariously, it upgraded from 1.5.x just as I was working on this and it introduced some new error so this migration is totally in beta).
Strategically, the idea is ReactXP can already directly target the web via React (in addition to native iOS, Android, and Windows via React Native). So we just target the web with ReactXP, modify the web page loader to run in Electron, and modify some code so it doesn't assume it's running at the root of a web server.
Make TodoList into a web app. Run it on a local dev web server
First, use git to clone the ReactXP repository:
git clone https://github.com/microsoft/reactxp
In the
reactxp/samples
directory, you'll find the TodoList app. Open it:cd reactxp/samples/TodoList/
Install Electron into it:
npm install --save-dev electron
Now we need a web page to load the app into Electron. We'll use the default one from Electron quick start. Just copy the main.js from electron-quick-start into the TodoList folder, and rename it to
electron-main.js
just for clarity.Edit
electron-main.js
and change the function callmainWindow.loadFile('index.html')
to
mainWindow.loadURL("http://localhost:8080")
That way it'll load the ReactXP web app from the development server that npm starts later.
Modify the "main" entry in
package.json
to point to "electron-main.js", i.e. change it to:"main": "electron-main.js"
"scripts": {
...
"electron": "electron .",
...
}
Now we're ready to compile the TypeScript code:
npm run start-web
After running that once, if you make changes to the files in the
src
folder but not package.json
, you can recompile your TypeScript code more quickly by just running:npm run gulp-web
(See the
package.json
scripts to find out why this is faster than the start-web
command.)
The TypeScript app is now compiled into JavaScript in the web folder. Gulp will keep running to recompile any modified TypeScript files.
To server the web app in a local dev web server, open a new terminal in TodoList folder and run:
To server the web app in a local dev web server, open a new terminal in TodoList folder and run:
node nodeserver.js
Now to load the app into Electron, open yet another new terminal window in TodoList and run:
npm run electron
This method gives you quick development turnaround time as gulp will recompile your TypeScript code, the node server serves it live, and Electron and reload it live. But in production, you probably wouldn't ship your app this way. It'd need to be compiled to static files loaded directly by Electron.
Production build: static files, relative paths
TodoList/web/index.html
is used. Just modify electron-main.js
to load that instead: i.e. replace the function call tomainWindow.loadURL("http://localhost:8080")
with:
mainWindow.loadFile('web/index.html')
Unfortunately, by default, React builds uses absolute paths as if your web app is running from the root directory. This is true of ReactXP as well. So the app will fail to load in Electron as the app obviously can't be expected to sit in the root directory of your PC.
To fix this, we have to fix
web/index.html
(which appears to be hand-built rather than compiled in this sample app), fix some TypeScript code, and fix package.json
to configure React to use relative paths.Open
web/index.html
and change every href
and src
attribute to use relative instead of absolute paths. This means replacing everyhref="/
with (note the dot)
href="./
And also replace every
src="/
with (note the dot again)
src="./
Then open
TodoList/src/ts/app/AppConfig.ts
and modify the getDocRoot()
function to returnreturn window.require('electron').remote.app.getAppPath()+'/web/';
so that any TypeScript code using that function will instead get an absolute path that is relative to the
TodoList/web
folder, wherever TodoList happens to be in the file system.Then open
package.json
and add a new "homepage" property to point to the current directory. I added it right after "main", which we've modified previously:"main": "electron-main.js",
"homepage":".",
Note the "homepage" property value is a single dot and not "./" (as I had originally thought). Apparently that makes a difference, and is an official thing in React (specifically react-scripts) [1].
Then you can compile as before by running
npm run start-web
or
npm run gulp-web
There is no more need to run the node local web server. Just run electron to run the app:
npm run electron
If you really want to use the development server, it's possible to write more code to make it easier switching between using the local server for development, and the static page for production builds (see [2]). I prefer to use the production static page even during development though.
Future work: production build bug fixes
Bug 1 (FIXED!):
This was fixed above! Namely editing
TodoList/src/ts/app/AppConfig.ts
and modify the getDocRoot()
function to returnreturn window.require('electron').remote.app.getAppPath()+'/web/';
instead of
return './'; // or '.'
so that any TypeScript code using that function will instead get an absolute path that is relative to the
TodoList/web
folder, wherever TodoList happens to be in the file system.ViewBase.js:115 GET file:///images/todo-logo.png net::ERR_FILE_NOT_FOUND
ViewBase.js:115 GET file:///images/todo-small.png net::ERR_FILE_NOT_FOUND
react-dom.development.js:3048 GET file:///images/todo-small.png net::ERR_FILE_NOT_FOUND
Bug 2 (FIXED!):
This was fixed in ReactXP as of 2019 Apr 6 with the latest version of ReactXP and its reactxp-virtuallistview 2.0.0.
ERROR in /Users/user_home/dev/reactxp/samples/TodoList/src/ts/views/TodoListPanel.tsx
./src/ts/views/TodoListPanel.tsx
[tsl] ERROR in /Users/user_home/dev/reactxp/samples/TodoList/src/ts/views/TodoListPanel.tsx(129,18)
TS2322: Type '{ itemList: TodoListItemInfo[]; renderItem: (details: VirtualListCellRenderDetails<TodoListItemInfo>) => Element; style: StyleRuleSet<ViewStyle>; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<VirtualListView<TodoListItemInfo>> & Readonly<VirtualListViewProps<TodoListItemInfo>> & Readonly<{ children?: ReactNode; }>'.
Property 'style' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<VirtualListView<TodoListItemInfo>> & Readonly<VirtualListViewProps<TodoListItemInfo>> & Readonly<{ children?: ReactNode; }>'.
References
I learned from many others, but oddly most only write on one aspect or another but don't put the whole thing together from top to bottom like I did above, and especially nothing quite so comprehensively showing how to get a ReactXP app to run in Electron. Thanks to the following though!
facebook/create-react-app: Is there a way to remove first "/" from resources url? #1094
webpack-contrib/file-loader: How to deal with relative public path #46
Serving the Same Build from Different Paths.
[2] How to build an Electron app using create-react-app. No webpack configuration or “ejecting” necessary
[3] ReactXP Getting Started: Building Your First ReactXP App
[4] vicentedealencar/reactxp-electron
[5] electron/electron-quick-start
[6] How to build a Desktop Application with Electron and React
No comments:
Post a Comment