🎄
ReactコンポーネントをAngularで使う
はじめまして、@onicode3です。
AngularコンポーネントからReactコンポーネントを使う方法を紹介します。
Angular から React コンポーネントを使う実装方法
1. React パッケージのインストール
Reactをインストールします。
今回はテストの為、angularからreact-selectを表示してみます。
npm i react react-dom react-select
npm i -D @types/react @types/react-dom
2. tsconfig.json の設定
tsconfig.json に jsx の設定を追加します。
tsconfig.json
{
"jsx": "react-jsx"
}
3. ライブサイクルフックでReactコンポーネントをレンダリングする
Angularのライフサイクルフックを使って、Reactコンポーネントをレンダリングします。
- afterNextRender(一回限りの初期化): コンテナとなるdomを取得し、Reactコンポーネントをレンダリングします。
- afterRender(すべての変更検出時): Reactコンポーネントを再レンダリングして、Angular側の変更を反映します。
- inject(DestroyRef).onDestroy(): Angularコンポーネントが破棄された時にReactコンポーネントもアンマウントします。
これで完成です。
const containerElementName = '__REACT_COMPONENT_REF__';
@Component({
selector: 'app-angular-react-select',
imports: [],
template: `
<div #${containerElementName}></div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AngularReactSelectComponent {
// ~~~
private reactRoot!: Root;
constructor() {
inject(DestroyRef).onDestroy(() => {
this.reactRoot.unmount();
});
afterNextRender({
write: () => {
this.reactRoot = createRoot(this.reactRootContainer().nativeElement);
this.render();
},
});
afterRender({
write: () => {
this.render();
},
});
}
// ~~~
}
完成したコード
今回はreact-selectをAngularから表示しています。
全体 ソースコード
angular-react-select.component.tsx
import {
afterNextRender,
afterRender,
ChangeDetectionStrategy,
Component,
DestroyRef,
ElementRef,
inject,
signal,
viewChild,
} from '@angular/core';
import { StrictMode } from 'react';
import { createRoot, Root } from 'react-dom/client';
import Select, { Options, SingleValue } from 'react-select';
const containerElementName = '__REACT_COMPONENT_REF__';
export interface SelectOptionValue {
value: string;
label: string;
}
const options = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' },
{ value: 'vanilla', label: 'Vanilla' },
];
@Component({
selector: 'app-angular-react-select',
imports: [],
template: `
<p>angular-react-select works!</p>
<button (click)="addClick()">Add Option</button>
<div #${containerElementName}></div>
<div>{{ selectValue()?.label }}</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AngularReactSelectComponent {
private readonly reactRootContainer =
viewChild.required<ElementRef<HTMLDivElement>>(containerElementName);
public options = signal<Options<SelectOptionValue>>(options);
public selectValue = signal<SelectOptionValue | null>(options[0]);
private reactRoot!: Root;
constructor() {
inject(DestroyRef).onDestroy(() => {
this.reactRoot.unmount();
});
afterNextRender({
write: () => {
this.reactRoot = createRoot(this.reactRootContainer().nativeElement);
this.render();
},
});
afterRender({
write: () => {
this.render();
},
});
}
protected addClick = () => {
this.options.update((options) => {
return [...options, { label: 'Hello', value: 'hello' }];
});
};
private readonly handleChange = (value: SingleValue<SelectOptionValue>) => {
this.selectValue.set(value);
};
private readonly render = () => {
this.reactRoot.render(
<StrictMode>
<Select
defaultValue={this.selectValue()}
options={this.options()}
onChange={this.handleChange}
/>
</StrictMode>
);
};
}
Discussion