16

Building Native Desktop Apps with React Node GUI

 4 years ago
source link: https://blog.bitsrc.io/building-native-desktop-application-with-react-node-gui-2ce1b2a2164
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Building Native Desktop Application with React Node GUI

How to use your ReactJS skills to build desktop apps.

mUb6F36.png!web

Have you ever wondered if you can build a native and cross-platform desktop application with React? No, not by wrapping your web app inside a browser. But a REAL desktop application that is developed with React and is memory efficient.

If the answer is yes, then I have great news for you. Because today, You can use React Node GUI to build a React-based desktop app!

Just like Electron , Node GUI is a framework that you can use to build a desktop app with JavaScript. What’s different is that Node GUI is powered by Qt5 , a toolkit engine for building cross-platform applications for all kinds of operating systems.

Node GUI aims to get all the good parts about Electron like providing great developer experience and powerful native APIs, while efficiently consuming CPU and memory at the same time.

React Node GUI is an extension of Node GUI that renders the component tree you write into the desktop platform.

Features of React Node GUI include:

  • Cross-platform desktop app development, similar to React Native for mobile platform
  • Styling using CSS with support for flexbox layout
  • First-class TypeScript support
  • Low CPU and memory footprint
  • Complete NodeJS API support (from version 12.X and up)

This article will help you get started on developing desktop apps with React Node GUI. Let’s start by installing the requirements to run the framework and write a Hello World app .

Please note that React Node GUI is under active development. It’s recommended to avoid using it for any complex application until it reached version one.

Reusing Components for Cross-Platform Dev

The best thing about building EVERYTHING with React is that you get to maximize your code reuse like never before.

It’s not only a way to speed up development and build more scalable apps, but quite simply, a way to make coding more pleasurable. — Repeated tasks should be left for machines.

To make you reusable React components available for all projects and repositories, use cloud component hubs like Bit.dev .

Bit.dev makes it easy to publish components (to Bit’s registry) from any project you’re currently working on. In addition to that, you get all the tools you need to document and organize your components so that you and your team will be able to find what you’re looking for as fast as possible.

Bit.dev supports both React and React with TypeScript.

uyM3Ejm.jpg Exploring React components published on Bit.dev

Installing React Node GUI requirements

To develop apps with React Node GUI, you need to install some required software:

  • NodeJS at least version 12.x
  • CMake for generating Node GUI’s build files when you install it
  • Make and GCC v7 for compiling Node’s native library in Mac and Linux

And other software depending on your current OS. For Mac, you need to have macOS 10.10 (Yosemite) and up. For Windows, you need to install Visual studio 2017 and up to get GCC v7 installed.

Please look into the documentation to see what is required for your local machine.

You also need to have experience in developing applications with React.

Hello World from Node GUI

To create a React Node GUI application, you only need three things:

package.json
tsconfig.json
.tsx

Let’s start by writing the package.json file. Create a new directory named react-nodegui-hello-world and copy the content below:

{
"name": "react-nodegui-hello-world",
"version": "1.0.0",
"description": "",
"main": "dist/index.js",
"scripts": {
"start": "tsc && qode dist/index.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"@nodegui/nodegui": "^0.19.0",
"@nodegui/react-nodegui": "^0.6.1",
"react": "^16.9.0"
},
"devDependencies": {
"@types/node": "^13.13.5",
"@types/react": "^16.9.2",
"@types/react-reconciler": "^0.18.0",
"typescript": "^3.5.3"
}
}

Now run npm install and I will explain the package to you while we wait for the installation to finish.

Basically, you need NodeGUI package because it’s the main library that takes care of creating a desktop app from JavaScript code. Qode is a modified runtime that allows Node’s event loop to be merged with Qt’s event loop. It’s the main engine that will run your application.

TypeScript is required because you need to compile your React code before running it with Qode. If you don’t use TypeScript, you need to configure Babel and set its target to ES 2016 first.

Once your installation is complete, create an index.tsx file and write your first desktop app component:

import React from "react";
import { Renderer, Window, Text } from "@nodegui/react-nodegui";
const App = () => {
return (
<Window>
<Text>Hello World!</Text>
</Window>
);
};
Renderer.render(<App />);

Here you import the Renderer, which components from react-nodegui:

The Renderer will render your component into the desktop’s native widget, similar to how ReactDOM renders component into the DOM. The <Window> component is the root component of your app, and <Text> component is like a label component that you can use to display text.

Unlike using React for the web, in nodegui you can’t use regular HTML elements. Rather, you use special components that have been created for the framework.

With the component ready, you need to write a tsconfig.json file so that TypeScript can compile your code correctly. You can use the config already provided from the starter project . But to make the project clean, you need to put the output into a separate dist/ directory

{
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "jsx": "react",
    "strict": true,
    "alwaysStrict": true,
    "moduleResolution": "node",
    "esModuleInterop": true,
    "outDir": "./dist" /* set the output directory */
  },
  "include": ["**/*"]
}

Finally, run npm start to compile the code and run the engine. You will see your OS native window with a “Hello World!” text written in it. Congratulations!

eI3AVjF.png!web

Your first React Node GUI app

If you encounter any errors, you can verify your code is correct with my repo over here .

Building a GitHub Search App

Now that you know how to React Node GUI works, let’s build a slightly more advanced app to fetch data from GitHub API .

To follow along, you need to clone the repo for react-nodegui starter that I have prepared for this tutorial. It already has webpack build and hot reload configured so that your app will automatically reload in development. I’ve also removed TypeScript to make this tutorial more beginner-friendly.

As always, run npm install first. Using this app requires you to open two terminals: one for running the webpack build:

npm run dev

And another for starting your application:

npm start

If you encounter any error along the way, you can check on the complete code and compare it with your own code. Let’s start right away.

Writing the layout

The layout of the application will look similar to this:

IN3qQr7.png!web

GitHub Search App desktop app layout

First, let’s write down the stylesheet required for this app. You need to create a variable to contain all of your CSS code. Open your app.tsx and write the following content into it:

const styleSheet = `
  #container {
    flex: 1;
    padding: 20px;
  }
  #search-bar {
    flex-direction: 'row';
    justify-content: 'space-around';
    align-items: 'center';
    margin-top: 20px;
    margin-bottom: 20px;
  }
  #search-input {
    flex: 1;
  }
  #card-row {
    flex-direction: 'row';
    background-color: 'white';
    border-bottom: 1px solid blue;
  }
  #user-img {
    height: 100px;
    width: 100px;
    padding-right: 10px;
  }
  #user-detail-row {
    flex:1;
    flex-direction: 'column';
    justify-content: 'space-around';
  }
  #button-profile {
    width: 150px;
  }
`;

The CSS above will create a simple layout that follows the design we’ve seen earlier. Since nodegui support flexbox layout, we will use it to arrange the layout of the app.

There is no className prop because our app doesn’t run in the browser. The id props that we write are mapped into Qt’s objectName props, and since Qt support assigning the same objectName to multiple components, it’s quite similar to how classes work in the browser.

It’s time to write the components to render inside the application. We’ll start by creating the outer layout, which is a Window , a ScrollArea and a View :

import React from 'react';
import {
hot,
Window,
Text,
View,
LineEdit,
Button,
Image,
ScrollArea,
} from '@nodegui/react-nodegui';
const App = () => {
return (
<Window
windowTitle='GitHub Search App'
minSize={{width: 550, height: 450}}
styleSheet={styleSheet}>
<ScrollArea>
<View id='container'>
</View>
</ScrollArea>
</Window>
);
};

Next, create the three inner View:

const App = () => {
return (
<Window
windowTitle='GitHub Search App'
minSize={{width: 550, height: 450}}
styleSheet={styleSheet}>
<ScrollArea>
<View id='container'>
<View id='header'>
</View>
<View id='search-bar'>
</View>
<View id='card-row'>
</View>

</View>
</ScrollArea>
</Window>
);
};

Then we write the inner components for each View . For header , we’ll write some Text components:

<View id='header'>
  <Text>GitHub Search App</Text>
  <Text>Search users in GitHub using this simple app</Text>
  <Text>Click on the card to see more detail about individual user.</Text>
</View>

For the search-bar, we’ll write a LineEdit and a Button component. LineEdit is a one-line text editor that works just like a text input:

<View id='search-bar'>
  <LineEdit
    placeholderText='Type username here and press Enter'
    id='search-input'
  />
  <Button text='Submit' />
</View>

For the card-row , we’ll create a View that contains an Image component and another View . The inner View will have two Text components and a Button component:

<View id='card-row'>
<Image
id='user-img'
src="https://avatars0.githubusercontent.com/u/10413648?v=4"
/>
<View id='user-detail-row'>
<Text>Username: nsebhastian</Text>
<Text>Score 1</Text>
<Button id='button-profile' text='Go to profile'/>
</View>
</View>

We’ll use dummy data for now. Later, we will map data dynamically from GitHub API response for each card-row component. Here is the entire App component code:

const App = () => {
return (
<Window
windowTitle='GitHub Search App'
minSize={{width: 550, height: 450}}
styleSheet={styleSheet}>
<ScrollArea>
<View id='container'>
<View id='header'>
<Text>GitHub Search App</Text>
<Text>Search users in GitHub using this simple app</Text>
<Text>Click on the card to see more detail about individual user. </Text>
</View>
<View id='search-bar'>
<LineEdit
placeholderText='Type username here and press Enter'
id='search-input'
/>
<Button text='Submit' />
</View>
<View id='card-row'>
<Image
id='user-img'
src="https://avatars0.githubusercontent.com/u/10413648?v=4"
/>
<View id='user-detail-row'>
<Text>Username: nsebhastian</Text>
<Text>Score 1</Text>
<Button id='button-profile' text='Go to profile'/>
</View>
</View>
</View>
</ScrollArea>
</Window>
);
};

Saving user input to state

Our layout is complete. Now we need to use React state to store the value of our LineEdit component. Let’s import useState from react and use it inside our component. Since the state will be used as the keyword for our search, we’ll call it the keyword state:

import React, { useState } from 'react';//... other codesconst App = () => {
const [keyword, setKeyword] = useState('');

Next, update our LineEdit component and pass the state as the value of text props:

<LineEdit
  placeholderText='Type username here and press Enter'
  id='search-input'
  text={keyword}
/>

One more thing: we need to update keyword state value when users type on the input box. In React, you may have used onChange event handler props to do it.

In React Node GUI, you need to use the on event handler props, which accepts an object map with event type as key and a callback function as its value.

Here, we will make LineEdit listen to the textChanged event and pass it the setKeyword function:

<LineEdit
placeholderText='Type username here and press Enter'
id='search-input'
text={keyword}
on={{textChanged: setKeyword}}
/>

With this, the keyword value will be updated any time you enter or delete a character from the text input.

Calling GitHub API on submit

Let’s continue with working on the submit button. We’ll make it call on GitHub API when clicked, and we’ll store the response from GitHub into a state.

We’ll start by creating a new state for the response. Since the response will return an array of users and their data, we’ll name the state users and initialize it with an empty array:

const App = () => {
const [keyword, setKeyword] = useState('');
const [users, setUsers] = useState([]);

We need to create a function to fetch users from GitHub. To do that, we have to install axios so that we can make an HTTP request to GitHub:

npm install axios

Once the installation is complete, we’ll write a function to send a GET request into GitHub’s search users API . We’ll store the response into the users state:

const fetchUsers = username => {
let url = `https://api.github.com/search/users?q=${username}`;
axios
.get(url)
.then(res => {
setUsers(res.data.items)
})
.catch(error => console.log('Oops! . There Is A Problem' + error));
};

Now we only have to call fetchUsers each time the submit button is clicked. Update your submit button component with an on event handler props:

<Button text='Submit' on={{
clicked: () => {
fetchUsers(keyword)
}
}}
/>

The response of GitHub API is now stored in users state.

Mapping GitHub data into the card rows

Let’s map the users state, which contains an array of users data, into the card-row component. To do that, we can use map function and render a card-row for each item in the array:

{
  users.map((item) => {
    return (
      <View key={item.id} id='card-row'>
        <Image
          id='user-img'
          src={item.avatar_url}
        />
        <View id='user-detail-row'>
          <Text>{`Username: ${item.login}`}</Text>
          <Text>{`Score: ${item.score}`}</Text>
          <Button id='button-profile' text='Go to profile'/>
        </View>
      </View>
      );
  })
}

We’ll put the user’s avatar_url as the src of the Image component, while the two Text components will render user’s username and score respectively. You can open the app to see it fetch users and render it into the card rows now.

Opening GitHub profile in the browser

Finally, our app is almost complete. The only thing left to do is to make the “Go to profile” button to work. To do that, we need a library that can open a browser window and navigate into a specific URL.

Let’s use the open library to do just that:

npm install open

Then, add an on event clicked props into the button:

<Button id='button-profile' text='Go to profile' 
on={{ clicked: () => { open(item.html_url) } }}
/>

And that’s it. Now try to run your application, and a browser will be opened when you click on the “Go to profile” button. Nice!

Conclusion

You have just learned about React Node GUI and how to use its built-in component to render data that is fetched from an API.

Although it’s not yet stable, React Node GUI is a unique approach to building a native and cross-platform desktop application that enables you to truly create a React-based desktop app without using a browser engine under the hood.

It’s definitely great to see more effort in making cross-platform desktop development easier. If you’re interested in React Node GUI, don’t forget to check its documentation and examples to see what kind of app you can build with it.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK