Skip to content

Instantly share code, notes, and snippets.

@brandonroberts
Forked from lacolaco/1.ngx-reactify.tsx
Created August 16, 2022 13:24
Show Gist options
  • Save brandonroberts/e04c7a59b96fd3b720e2dd5ea71ec95a to your computer and use it in GitHub Desktop.
Save brandonroberts/e04c7a59b96fd3b720e2dd5ea71ec95a to your computer and use it in GitHub Desktop.
A React component to render a standalone Angular component (Angular v14.2 is required)
import { ComponentRef, createComponent, Type } from "@angular/core";
import { createApplication } from "@angular/platform-browser";
import React, { useEffect, useRef, useState } from "react";
type AnyComponentRef = ComponentRef<unknown>;
export type ReactifyProps = {
component: Type<unknown>;
inputs?: Record<string, unknown>;
};
export const Reactify: React.FunctionComponent<ReactifyProps> = (props) => {
const hostElementRef = useRef<HTMLDivElement>(null);
const [componentRef, setComponentRef] = useState<AnyComponentRef | null>(
null
);
const renderComponent = async () => {
const appRef = await createApplication();
return createComponent(props.component, {
environmentInjector: appRef.injector,
hostElement: hostElementRef.current!,
});
};
const changeInputs = () => {
if (componentRef) {
for (const [key, value] of Object.entries(props.inputs || {})) {
componentRef.setInput(key, value);
}
componentRef.changeDetectorRef.detectChanges();
}
};
useEffect(() => {
renderComponent().then(setComponentRef);
}, [hostElementRef, props.component]);
useEffect(() => {
changeInputs();
}, [componentRef, props.inputs]);
return <div ref={hostElementRef} />;
};
import { Component, Input } from "@angular/core";
import { render, screen, waitFor } from "@testing-library/react";
import React from "react";
import { Reactify } from "./reactify";
describe("Reactify", () => {
it("should render a standalone Angular component", async () => {
@Component({
template: `<div>TEST</div>`,
standalone: true,
})
class TestComp {}
render(<Reactify component={TestComp} />);
await waitFor(() => {
expect(screen.getByText("TEST")).toBeTruthy();
});
});
it("should render a standalone Angular component with inputs", async () => {
@Component({
template: `<div>{{ a }}-{{ b }}</div>`,
standalone: true,
})
class TestComp {
@Input() a = "";
@Input() b = "";
}
render(<Reactify component={TestComp} inputs={{ a: "foo", b: "bar" }} />);
await waitFor(() => {
expect(screen.getByText("foo-bar")).toBeTruthy();
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment