Standalone application with Electron, React, and SQLite stack.
Sometimes we need to build some kind of application that does not need to consume any online service and we need to store data at the same time. Local storage could be an option in some cases, but a few weeks ago I’ve faced a project requirement where the application must store massive data collected in field research at remote places with no internet connection.
The collectors were using a kind of windows application built with the C# Windows Forms framework and SQLite that I created. With that stack, it’s simple to open a file or even connect to a database, as the process who runs the application is the same main process that launched it.
Of course, the collectors came back to me asking about new requirements on the application, and as I’ve learned and improved much more my skills I decided to rewrite the small application using that new stack proposed at the story title.
TL;DR;
Let’s get to what lights up our eyes
First of all, we must use create-react-app to bootstrap our application and then install some dependencies related to the Electron.
create-react-app appyarn add electron electron-builder wait-on concurrently --devyarn add electron-is-dev
Add this electron.js file to the “public” folder.
Add the “main” property to package.json and point to our electron file:
“main”: “public/electron.js”
Add this script to run the dev version:
"electron-dev": "concurrently \"BROWSER=none yarn start\" \"wait-on http://localhost:3000 && electron .\""
Run the script:
yarn electron-dev
Now you have a sample electron app running. It’s basically what a guy named Kitze wrote here.
What now? How to connect to an SQLite data source?
At this point, We’re about to use something named Electron ipcMain and ipcRenderer. They are both a Node.js Event Emitter. Essentially we use them to communicate between the main process to the render process and vice-versa.
Having these tools on our hands, let’s “ping pong” it.
Write a function to allow the renderer to say ping to the main process. It will send a message named asynchronous-message to the main process and will be waiting once for a response named asynchronous-reply.
As our renderer.js will be imported by the Electron renderer process, we must explicit we want Node integration at our created BrowserWindow. So, get back to electron.js and put this there.
new BrowserWindow({
...
webPreferences: {
nodeIntegration: true,
}
});
That’s how the electron dependencies are injected into the browser window. Afterward, we import the dependency from the window variable as you could see on the renderer.js file.
const electron = window.require('electron');
Now, write a function to allow the main process to receive the renderer message (asynchronous-message) and respond it back (asynchronous-reply).
Import the main.js on our electron.js file. Remember that file, right?
// preferable require it right after the main imports.
require('../src/message-control/main');
On our web application (the renderer), create basic logic just to see that behavior happening.
Awesome, we already have our web application communicating to the main process, which is so powerful in terms of access.
You may say: “Ok, we’ve already played a lot with it but you didn’t show me anything about SQLite yet.”
Yeah, I did this on purpose to make sure you understood the concept of the communication between both processes (main and renderer).
So, talking about SQLite… add it.
yarn add sqlite3
Tip: Add an SQLite database file to the public folder. I suggest you use the DB Browser for SQLite tool.
Change the main.js to receive a SQL statement, run it, and send the result back to the renderer via event.
Change the App.js to treat the result as an array, as below:
Now, you must see the query results on the app screen. Simple as it is.
Ok! That’s all folks.
Did you get a kind of ‘module not found’ error about SQLite?
That’s because sqlite3 isn’t a native module, as in Node.js only 400 modules are native, against the 650,000 modules available.
https://www.electronjs.org/docs/tutorial/using-native-node-modules
But, if you wanna solve that, repeat the following steps:
yarn add --dev electron-rebuild
Add to your package.json:
“rebuild-sqlite3”: “electron-rebuild -f -w sqlite3”
Run:
yarn rebuild-sqlite3
It could be a bit tricky. If you are on a MacOSX, you might need to have XCode and its tools installed. Also, on Windows, you may need to have some of the .NET Framework properly installed to build that native module. I don’t want to talk about that part of the story to avoid distraction from the main story’s idea.
Conclusion
With that stack, every React web developer can build desktop solutions collecting and storing data into an improved data source once compared to the simple and limited local storage.
I hope you all enjoyed it. You can check the repository at https://github.com/fmacedoo/ers-stack
Thanks!