Reactron — Criando um aplicativo com o React e o Electron
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étodo
http.request
, faço a chamada; - A resposta é retornada a partir do método
res.on
, atualizando athis.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