Skip to content

Instantly share code, notes, and snippets.

@lacolaco
Last active February 26, 2024 08:13
Show Gist options
  • Star 28 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save lacolaco/8e81f61d82327b42f664cdc7080761ac to your computer and use it in GitHub Desktop.
Save lacolaco/8e81f61d82327b42f664cdc7080761ac to your computer and use it in GitHub Desktop.
A React component to render a standalone Angular component (Angular v14.2 is required)
import { ApplicationRef, 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 hostRef = useRef<HTMLDivElement>(null);
const [appRef, setAppRef] = useState<ApplicationRef | null>(null);
const [compRef, setCompRef] = useState<AnyComponentRef | null>(null);
useEffect(() => {
createApplication().then(setAppRef);
return () => appRef?.destroy();
}, []);
useEffect(() => {
if (appRef && hostRef.current) {
setCompRef(
createComponent(props.component, {
environmentInjector: appRef.injector,
hostElement: hostRef.current!,
})
);
}
return () => compRef?.destroy();
}, [appRef, hostRef, props.component]);
useEffect(() => {
if (compRef) {
for (const [key, value] of Object.entries(props.inputs || {})) {
compRef.setInput(key, value);
}
compRef.changeDetectorRef.detectChanges();
}
}, [compRef, props.inputs]);
return <div ref={hostRef} />;
};
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