Reactron — Criando um aplicativo com o React e o Electron

Ricardo Mansano Godoi
TOTVS Developers
Published in
6 min readOct 7, 2019

Antes de tudo queria acalmá-los. Este post é longo devido à grande quantidade de código (gists), mas seu conteúdo didático foi o mais resumido que consegui fazer…

O material em vídeo sobre este post pode ser acessado aqui.

Momento Jabá:
Acesse developers.totvs.com pra ficar antenado às mais diversas tecnologias utilizadas na TOTVS, e também nossas vagas, meetups, vídeos, podcasts e muito material bacana compartilhado por colegas e parceiros.

Todos nós involuntariamente conhecemos o Electron. Ele é a plataforma sob a qual foram construídos aplicativos como os editores VSCode e Atom, e versões desktop do Slack, Skype e muitos outros.

A ideia do Reactron é disponibilizar um MVP para criação de mini aplicativos multiplataforma, compilando seus projetos em Linux, Windows, Mac e até mesmo para ARMs, utilizando o mesmo código fonte HTML5.

Assim, será possível criar soluções para as mais diferentes finalidades, que poderão também se integrar ao ecossistema Protheus, via Rest, por exemplo.

Resolvi utilizar o React como framework para estudá-lo e confesso que gostei, porém este projeto (com certo esforço) pode ser alterado para outros frames. Como interface, utilizei o Material UI.

O projeto Electrate me ajudou muito no start e, preenchendo algumas lacunas, concluí minha ideia.

Sabem que detesto enrolação, então vamos por a mão na massa…

Como o Reactron é um MVP, basta seguirem a receita de bolo abaixo, mas caso queriam iniciar um projeto do zero, utilizando o Electron, este link ira ajudar.

Os pré-requisitos são:
Git
Node e NPM
VSCode vai ajudar

Acesse aqui a página do projeto no Git.

Instalação

Crie uma pasta para seu projeto, e via linha de comando:

# Clone o repositório
git clone https://github.com/ricardomansano/reactron.git reactron

# Acesse o diretório
cd reactron

# Instale as dependências
npm install
# Inicie o projeto
npm start

Se tudo correr bem, o aplicativo será iniciado; repare que no canto superior direito temos a referência do código fonte, facilitando o estudo:

Vamos navegar nas opções do menu e conversar sobre o código fonte.

Formulário

Na opção formulário (basic-form.js) temos o mesmo exemplo utilizado no post React pra quem vem do AdvPL. Então, quem leu este artigo estará familiarizado com o trecho.

Neste fonte temos uma classe pai (BasicForm) e uma filha (BasicItem), controlando o insere/deleta dos itens contidos no this.state.data, um array na classe pai.

Executa comando

Aqui executamos comandos do SO através do back-end, no caso o NodeJS através do próprio Electron.

Fazemos isso, solicitando acesso ao back-end via front(commands.js):

// Permite acesso a funcoes do back-end
const { remote } = require('electron')
const backend = remote.require('./main.js')

No onClickSubmit chamamos a função execProcess, que esta no back-end (commands.js):

onClickSubmit = (e) => {
backend.execProcess(this.state.command, this.execProcessCallBack)
e.preventDefault();
}

O back-end executa o comando e retorna as informações, ou o erro, via callBack (main.js):

exports.execProcess = (process, callback) => {
const { exec } = require('child_process');
const callExec = exec(process)

callExec.stdout.on('data', function(data){
callback(data)
})
callExec.stderr.on('data', function(data){
callback("<b>ERROR:</b> \n" + data)
})
}

Repare que no main.js eu exporto a função através do trecho exports.execProcess, permitindo seu uso pelo front-end.

Outro processo de integração entre o back e o front-end pode ser estudado através dos links abaixo:
https://electronjs.org/docs/api/ipc-main
https://electronjs.org/docs/api/ipc-renderer

Por fim, o callback (execProcessCallBack) é instanciado através do back-end, e o resultado exibido em tela (commands.js).

execProcessCallBack = (str) => {
document.getElementById(“execProcessCallBack”).innerHTML += “<pre>”+str+”</pre>”
}

Consumindo Rest

No consumo de serviços Rest é onde se torna possível integrar o Reactron ao Protheus e outros serviços, trocando informações entre as plataformas.

Para facilitar o teste, ao clonar o projeto, disponibilizei um mini rest server, que está na pasta sample_rest, para executá-lo:

cd sample_rest
npm install
node index.js
Server running on port: 3333

O consumo é feito a partir da função callRest(rest.js), da seguinte maneira:

  • A variável options contém os dados que serão passados na solicitação;
  • Com o métodohttp.request, faço a chamada;
  • A resposta é retornada a partir do método res.on, atualizando a this.state.return_rest, que será exibida em tela;
  • No caso de erro, o método req.on('error' retornará esta informação.

Manipulação de arquivos

Na manipulação de arquivos temos dois exemplos, através da opção SELECIONA ARQUIVOS. Abrimos um file manager e exibimos os arquivos selecionados em tela.

No código fonte este exemplo está separado da seguinte maneira:

# Na camada HTML invocamos a função selectFiles após a seleção
<input
accept="all/*.*"
style={{ display: 'none' }}
id="raised-button-file"
multiple
type="file"
onChange={() => {this.selectFiles(event)}}/>
# Na camada JavaScript exibimos os arquivos alterando o State da variável filesOpened
selectFiles = (event) => {
var input = event.target;
var files = input.files;
// "Varre" os arquivos selecionados
let cFiles = ""
for (var i = 0; i < files.length; i++) {
cFiles += files[i].path + '\n'
}
// Atualiza state com os arquivos selecionados
this.setState({ filesOpened: cFiles})
}

Na opção CRIA/ABRE ARQUIVO INI também temos dois pontos:

# No Constructor verificamos a existência do arquivo INI
// Se Ini nao existir, cria
fs.exists(fName, (exists) => {
if (!exists){
fs.writeFileSync(fName, "[files]")
console.log(fName + " criado com sucesso!")
}
});
# Na função abaixo inserimos as Tags no arquivo INI, mais uma vez exibindo o resultado em tela
fileSystem_Ini
= () => {
// Abre arquivo reactron.ini no mesmo nivel do executavel
var fIni = ini.parse(fs.readFileSync(fName, ‘utf-8’))
// Insere linhas no INI
fIni.Escopo = ‘variavel de Escopo’ // Uma variavel sem Tag pode ficar no escopo
fIni.files.file1=”file1.png”
fIni.files.file2=”file2.png”
fIni.files.file3=”file3.png”
// Deleta file2
delete fIni.files.file2
// Salva arquivo INI
fs.writeFileSync(fName, ini.stringify(fIni))
// Atualiza state com o conteudo do arquivo ini
this.setState({ iniContent: ini.stringify(fIni)})
}

Aqui o fonte fs_inifile.js completo:

Compilação para distribuição

Para distribuir um aplicativo feito com o Electron você precisa compilá-lo, este processo vai depender de como foi configurado seu package.json. Uma maneira simples de verificar é através do comando npm run:

Neste link tem mais informações sobre a configuração dos tipos de build permitidos para cada Sistema Operacional.

npm runLifecycle scripts included in reactron: 
postinstall
install-app-deps
start
gulp

available via `npm run-script`:
build
gulp build
dist
gulp dist

Para compilação, vamos usar a opção dist, de distribuição.

npm run dist

Ao término do processo você terá o conjunto de arquivos do aplicativo pronto pra distribuição na pasta dist de seu projeto.

Conclusão

Como devem ter percebido, o Electron é bem simples de lidar, ainda mais se explicado como neste projeto ;).

Brincadeira à parte, como sempre, espero que aproveitem o material e, qualquer dúvida, sabem onde me encontrar…

Revisão: www.linkedin.com/in/robsonwilliam

--

--

Ricardo Mansano Godoi
TOTVS Developers

Chief engineer of Front-end and Development Tools on TOTVS, developing software since 88, plugged to the brand new technologies and Nerd to the bone.