Eu tenho um problema O aplicativo é escrito em Angular e a biblioteca de componentes em React. Clone uma biblioteca muito cara. Portanto, você precisa usar os componentes React em um aplicativo Angular com custo mínimo. Nós descobrimos como fazê-lo.
Isenção de responsabilidade
Não sou especialista em Angular. Tentei a primeira versão em 2017, depois olhei um pouco para o AngularDart e agora me deparei com o suporte a aplicativos em uma versão moderna do framework. Se lhe parecer que as decisões são estranhas ou "de outro mundo", isso não lhe parece.
A solução apresentada no artigo ainda não foi testada em projetos reais e é apenas um conceito. Use-o por sua conta e risco.
O problema
Agora, apoio e desenvolvo um aplicativo bastante grande no Angular 8. Além disso, existem alguns aplicativos pequenos no React e planeja criar uma dúzia a mais (também no React). Todos os aplicativos são usados para as necessidades internas da empresa e devem ter o mesmo estilo. A única maneira lógica é criar uma biblioteca de componentes com base na qual você pode construir rapidamente qualquer aplicativo.
Mas na situação atual, você não pode simplesmente pegar e escrever uma biblioteca no React. Você não pode usá-lo no maior aplicativo - está escrito em Angular. Eu não sou o primeiro a encontrar esse problema. A primeira coisa a estar no Google para "reagir em ângulo" é o repositório da Microsoft . Infelizmente, esta solução não possui documentação. Além disso, no leia-me do projeto, diz-se claramente que o pacote é usado internamente pela Microsoft, a equipe não tem planos óbvios para seu desenvolvimento e é de seu próprio risco usá-lo. Não estou pronto para arrastar um pacote tão ambíguo para a produção, então decidi escrever minha bicicleta.

Site oficial @ angular-react / core
Solução
React é uma biblioteca projetada para resolver um problema específico - gerenciar a árvore DOM de um documento. Podemos colocar qualquer elemento React em um nó DOM arbitrário.
const Hello = () => <p>Hello, React!</p>; render(<Hello />, document.getElementById('#hello'));
Além disso, não há restrições para chamadas repetidas para a função de render
. Ou seja, é possível renderizar cada componente separadamente no DOM. E é exatamente isso que ajudará a dar o primeiro passo na integração.
Preparação
Antes de tudo, é importante entender que o React, por padrão, não requer condições especiais durante a montagem. Se você não usa JSX, mas createElement
limita a chamar createElement
, não precisará tomar nenhuma providência; tudo funcionará imediatamente.
Mas, é claro, estamos acostumados a usar JSX e não queremos perdê-lo. Felizmente, o Angular usa o TypeScript por padrão, que pode transformar o JSX em chamadas de função. Você só precisa adicionar o sinalizador do compilador --jsx=react
ou em tsconfig.json
na seção compilerOptions
, adicionar a linha "jsx": "react"
.
Integração de exibição
Para começar, precisamos garantir que os componentes React sejam exibidos dentro do aplicativo Angular. Ou seja, para que os elementos DOM resultantes da biblioteca funcionem nos lugares certos na árvore de elementos.
Cada vez que você usa o componente React, é muito difícil pensar em chamar a função de render
corretamente. Além disso, no futuro, precisaremos integrar componentes no nível dos dados e nos manipuladores de eventos. Nesse caso, faz sentido criar um componente Angular que encapsulará toda a lógica de criação e controle do elemento React.
O código do componente Angular é extremamente simples. Ele próprio renderiza apenas um contêiner vazio e obtém um link para ele. Nesse caso, no momento da inicialização, a renderização do elemento React é chamada. Ele é criado usando a função createElement
e passado para a função de render
, que o coloca no nó DOM criado a partir de Angular. Você pode usar esse componente como qualquer outro componente do Angulat, sem condições especiais.
Integração de Entrada
Geralmente, ao exibir elementos da interface, você precisa transferir dados para eles. Tudo aqui também é bastante prosaico - ao chamar createElement
você pode passar quaisquer dados através dos adereços para o componente.
Agora você pode passar a string de name
para o componente Angular, ela cairá no componente React e será renderizada. Mas se a linha mudar devido a alguns motivos externos, o React não saberá sobre isso e obteremos uma exibição desatualizada. O Angular possui um ngOnChanges
ciclo de vida ngOnChanges
que permite rastrear alterações nos parâmetros dos componentes e reações a ele. Implementamos a interface OnChanges
e adicionamos um método:
Basta chamar a função render
novamente com um elemento criado a partir de novos objetos, e a própria biblioteca descobrirá quais partes da árvore devem ser renderizadas. O estado local dentro do componente também será preservado.
Após essas manipulações, o componente Angular pode ser usado da maneira usual e passar dados para ele.
<app-hello name="Angular"></app-hello> <app-hello [name]="name"></app-hello>
Para um trabalho mais preciso com a atualização de componentes, você pode olhar para a estratégia de detecção de alterações . Não vou considerar isso em detalhes.
Integração de saída
Outro problema permanece - a reação do aplicativo a eventos dentro dos componentes do React. Vamos @Output
decorador @Output
e passar o retorno de chamada para o componente por meio de adereços.
Feito. Ao usar o componente, você pode registrar manipuladores de eventos e responder a eles.
<app-hello [name]="name" (click)="sayHello($event)"></app-hello>
O resultado é um invólucro totalmente funcional para o componente React para uso em um aplicativo Angular. Ele pode transmitir dados e responder a eventos dentro dele.
Mais uma coisa ...
Para mim, a coisa mais conveniente no Angular é a Ligação de dados ngModel
, ngModel
. É conveniente, simples, requer uma quantidade muito pequena de código. Mas na implementação atual, a integração não é possível. Isso pode ser consertado. Para ser sincero, eu realmente não entendo como esse mecanismo funciona do ponto de vista do dispositivo interno. Portanto, admito que minha solução é super abaixo do ideal e ficarei feliz se você escrever nos comentários uma maneira mais bonita de dar suporte ao ngModel
.
Primeiro de tudo, você precisa implementar a interface ControlValueAccessor
(do pacote @angular/forms
e adicionar um novo provedor ao componente.
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms'; const REACT_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => HelloComponent), multi: true }; @Component({ selector: 'app-hello', template: `<div #react></div>`, providers: [PREACT_VALUE_ACCESSOR], }) export class PreactComponent implements OnInit, OnChanges, ControlValueAccessor {
Essa interface requer a implementação dos métodos onBlur
, writeValue
, registerOnChange
, registerOnTouched
. Todos eles estão bem descritos na documentação. Nós os realizamos.
Depois disso, você precisa garantir que tudo isso seja passado para o componente React. Infelizmente, o React não pode trabalhar com a Ligação de dados bidirecional, portanto, forneceremos um valor e um retorno de chamada para alterá-lo. renderReactElement
método renderReactElement
.
E no componente React, usaremos esse valor e o retorno de chamada.
export const Hello = ({ name, onClick, model }) => ( <div> <p>Hello, {name}!</p> <button onClick={onClick}>Say "hello"</button> <input value={model.value} onChange={e => model.onChange(e.target.value)} /> </div> );
Agora, realmente integramos o React ao Angular. Você pode usar o componente resultante como desejar.
<app-hello [name]="name" (click)="sayHello($event)" [(ngModel)]="name" ></app-hello>
Sumário
O React é uma biblioteca muito simples e fácil de integrar com qualquer coisa. Usando a abordagem mostrada, você pode não apenas usar componentes React em aplicativos Angular continuamente, mas também migrar gradualmente todo o aplicativo.
Neste artigo, não toquei nos problemas de estilo. Se você usa soluções clássicas CSS-in-JS (componentes estilizados, emoção, JSS), não precisa executar nenhuma ação adicional. Mas se o projeto exigir soluções mais produtivas (astroturf, linaria, CSS Modules), você precisará trabalhar na configuração do webpack. Reportagem - Personalize a configuração do Webpack em seu aplicativo angular .
Para migrar totalmente o aplicativo do Angular para o React, você ainda precisa resolver o problema de implementar serviços nos componentes do React. Uma maneira simples é obter o serviço em um componente de wrapper e transmiti-lo através de adereços. A maneira mais difícil é escrever uma camada que obterá serviços do injetor por token. Consideração desta questão além do escopo do artigo.
Bônus
É importante entender que, com essa abordagem de 85KB de Angular, quase 40KB de código de react
e de react-dom
são react
. Isso pode ter um impacto significativo na velocidade do aplicativo. Eu recomendo considerar o uso do Preact miniatura, que pesa apenas 3 KB. Sua integração quase não é diferente.
- No
tsconfig.json
adicionamos uma nova opção de compilação - "jsxFactory": "h"
, isso indica que você precisa usar a função h
para converter JSX. Agora, em cada arquivo com código JSX - import { h } from 'preact'
. - Todas as chamadas para
React.createElement
substituídas por Preact.h
. - Todas as chamadas para
ReactDOM.render
substituídas por Preact.render
.
Feito! Leia as instruções para migrar de React para Preact . Praticamente não há diferenças.
UPD 19.9.19 16.49
Link temático dos comentários - Micro Frontends
UPD 20.9.19 14.30
Outro link temático dos comentários - Micro Frontends