Skip to content

Commit f3670fa

Browse files
Christopher J Bakerzerico007@r2wc/react-to-web-component[bot]@workflow
authored
update for React 18 (#103)
* update for React 18 * move tests * fxx tests * update tests * prettier fix * add prop-types dev dep * socs update * update codesandbox example for legacy in docs * update header example link * link updates in docs * Publish @r2wc/react-to-web-component v2.0.0-alpha.1 * update links * update * update main documentation api link * update ReactDOMRenderType for legacy * prettier --------- Co-authored-by: Bavin Edwards <[email protected]> Co-authored-by: @r2wc/react-to-web-component[bot]@workflow <Workflow: @r2wc/react-to-web-component[bot]>
1 parent 60d3eca commit f3670fa

10 files changed

+146
-243
lines changed

README.md

+3-5
Original file line numberDiff line numberDiff line change
@@ -91,15 +91,13 @@ npm install @r2wc/react-to-web-component
9191

9292
## External Examples
9393

94-
Greeting example in a [CodePen](https://codepen.io/bavinedwards/pen/jOveaGm)
95-
9694
Greeting example in [CodeSandbox](https://codesandbox.io/s/sample-greeting-app-ts-qwidh9)
9795

9896
Hello, world example (React17) in [CodeSandbox](https://codesandbox.io/s/hello-world-react17-u4l3x1)
9997

10098
Example with all prop types in [CodeSandbox](https://codesandbox.io/p/sandbox/vite-example-with-numerous-types-gjf87o)
10199

102-
R2WC With Vite Header Example in [CodeSandbox](https://codesandbox.io/p/sandbox/header-example-e4x25q)
100+
R2WC With Vite Header Example in [CodeSandbox](https://codesandbox.io/p/sandbox/r2wc-header-example-vqzfgo)
103101

104102
## External Blog Posts
105103

@@ -109,9 +107,9 @@ R2WC with Create React App (CRA) [View Post](https://www.bitovi.com/blog/how-to-
109107

110108
## How it works
111109

112-
Check out our [full API documentation](../../docs/api.md).
110+
Check out our [full API documentation](https://github.com/bitovi/react-to-web-component/blob/main/docs/api.md).
113111

114-
`r2wc` creates a constructor function whose prototype is a [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). This acts as a trap for any property set on instances of the custom element. When a property is set, the proxy:
112+
Under the hood, `r2wc` creates a `CustomElementConstructor` with custom getters/setters and life cycle methods that keep track of the props that you have defined. When a property is set, its custom setter:
115113

116114
- re-renders the React component inside the custom element.
117115
- creates an enumerable getter / setter on the instance

package-lock.json

+39-20
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/legacy/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ R2WC with Create React App (CRA) [View Post](https://www.bitovi.com/blog/how-to-
106106

107107
## How it works
108108

109-
Check out our [full API documentation](../../docs/api.md).
109+
Check out our [full API documentation](https://github.com/bitovi/react-to-web-component/blob/main/docs/api.md).
110110

111111
# We want to hear from you.
112112

packages/legacy/src/react-to-webcomponent.ts

+3-7
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,7 @@ import type { R2WCOptions } from "@r2wc/core"
33
import r2wcCore from "@r2wc/core"
44

55
interface ReactType {
6-
createElement: (
7-
type: any,
8-
data: any,
9-
children?: any,
10-
) => React.ReactElement | null
6+
createElement: (type: any, data: any, children?: any) => React.ReactElement
117
}
128

139
interface ReactDOMRootRootType {
@@ -25,7 +21,7 @@ interface ReactDOMRootType {
2521
interface ReactDOMRenderType {
2622
unmountComponentAtNode: (container: Element | DocumentFragment) => boolean
2723
render: (
28-
element: React.ReactElement | null,
24+
element: React.ReactElement,
2925
container: ReactDOM.Container | null,
3026
) => unknown
3127
}
@@ -37,7 +33,7 @@ interface Context<Props> {
3733
}
3834

3935
/**
40-
* Converts a React component into a webcomponent by wrapping it in a Proxy object.
36+
* Converts a React component into a webcomponent by mounting it into an HTMLElement container.
4137
* @param {ReactComponent}
4238
* @param {React}
4339
* @param {ReactDOM}

packages/react-to-web-component/README.md

+16-3
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,11 @@ npm install @r2wc/react-to-web-component
9191

9292
## Examples
9393

94-
* [Greeting](https://codesandbox.io/s/greeting-react-17-u4l3x1)
95-
* [All the Props](https://codesandbox.io/s/all-the-props-react-17-x09rxo)
94+
* [Greeting](https://codesandbox.io/s/greeting-md5oih)
95+
* [All the Props](https://codesandbox.io/s/all-the-props-n8z5hv)
96+
* [Header Demo](https://codesandbox.io/s/example-header-blog-7k313l)
97+
* [MUI Button](https://codesandbox.io/s/example-mui-button-qwidh9)
98+
* [Checklist Demo](https://codesandbox.io/s/example-checklist-blog-y3nqwx)
9699

97100
## Blog Posts
98101

@@ -102,7 +105,17 @@ R2WC with Create React App (CRA) [View Post](https://www.bitovi.com/blog/how-to-
102105

103106
## How it works
104107

105-
Check out our [full API documentation](../../docs/api.md).
108+
Check out our [full API documentation](https://github.com/bitovi/react-to-web-component/blob/main/docs/api.md).
109+
110+
Under the hood, `r2wc` creates a `CustomElementConstructor` with custom getters/setters and life cycle methods that keep track of the props that you have defined. When a property is set, its custom setter:
111+
112+
- re-renders the React component inside the custom element.
113+
- creates an enumerable getter / setter on the instance to save the set value and avoid hitting the proxy in the future.
114+
115+
Also:
116+
117+
- Enumerable properties and values on the custom element are used as the `props` passed to the React component.
118+
- The React component is not rendered until the custom element is inserted into the page.
106119

107120
# We want to hear from you.
108121

packages/react-to-web-component/package.json

+5-4
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,12 @@
4545
"@r2wc/core": "^1.0.0"
4646
},
4747
"devDependencies": {
48-
"@types/react": "^17.0.0",
49-
"@types/react-dom": "^17.0.0"
48+
"@types/react": "^18.0.0",
49+
"@types/react-dom": "^18.0.0",
50+
"prop-types": "^15.8.1"
5051
},
5152
"peerDependencies": {
52-
"react": "^16.0.0 || ^17.0.0",
53-
"react-dom": "^16.0.0 || ^17.0.0"
53+
"react": "^18.0.0",
54+
"react-dom": "^18.0.0"
5455
}
5556
}

packages/react-to-web-component/src/react-to-web-component.test.tsx

+68-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/* eslint-disable @typescript-eslint/no-explicit-any */
22
import { describe, it, expect, assert } from "vitest"
33
import matchers from "@testing-library/jest-dom/matchers"
4+
import React from "react"
5+
import PropTypes from "prop-types"
46

57
import r2wc from "./react-to-web-component"
68

@@ -14,7 +16,7 @@ const Greeting: React.FC<{ name: string }> = ({ name }) => (
1416
<h1>Hello, {name}</h1>
1517
)
1618

17-
describe("react-to-web-component", () => {
19+
describe("react-to-web-component 1", () => {
1820
it("basics with react", () => {
1921
const MyWelcome = r2wc(Greeting)
2022
customElements.define("my-welcome", MyWelcome)
@@ -26,6 +28,71 @@ describe("react-to-web-component", () => {
2628
expect(myWelcome.nodeName).toEqual("MY-WELCOME")
2729
})
2830

31+
it("works with props array", async () => {
32+
function TestComponent({ name }: { name: string }) {
33+
return <div>hello, {name}</div>
34+
}
35+
36+
const TestElement = r2wc(TestComponent, { props: ["name"] })
37+
38+
customElements.define("test-hello", TestElement)
39+
40+
const body = document.body
41+
body.innerHTML = "<test-hello name='Bavin'></test-hello>"
42+
43+
await flushPromises()
44+
45+
const div = body.querySelector("div")
46+
expect(div?.textContent).toBe("hello, Bavin")
47+
})
48+
49+
it("works with proptypes", async () => {
50+
function WithProptypes({ name }: { name: string }) {
51+
return <div>hello, {name}</div>
52+
}
53+
54+
WithProptypes.propTypes = {
55+
name: PropTypes.string.isRequired,
56+
}
57+
58+
const WithPropTypesElement = r2wc(WithProptypes)
59+
60+
customElements.define("with-proptypes", WithPropTypesElement)
61+
62+
const body = document.body
63+
body.innerHTML = "<with-proptypes name='Bavin'></with-proptypes>"
64+
65+
await flushPromises()
66+
67+
const div = body.querySelector("div")
68+
expect(div?.textContent).toBe("hello, Bavin")
69+
})
70+
71+
it("works with class components", async () => {
72+
class TestClassComponent extends React.Component<{ name: string }> {
73+
render() {
74+
return <div>hello, {this.props.name}</div>
75+
}
76+
}
77+
78+
class TestClassElement extends r2wc(TestClassComponent, {
79+
props: ["name"],
80+
}) {}
81+
82+
customElements.define("test-class", TestClassElement)
83+
84+
const body = document.body
85+
body.innerHTML = "<test-class name='Bavin'></test-class>"
86+
87+
await flushPromises()
88+
89+
const div = body.querySelector("div")
90+
const testClassEl = body.querySelector("test-class")
91+
92+
expect(testClassEl).toBeInstanceOf(TestClassElement)
93+
expect(div?.textContent).toBe("hello, Bavin")
94+
})
95+
2996
it("works with shadow DOM `options.shadow === 'open'`", async () => {
3097
expect.assertions(5)
3198

packages/react-to-web-component/src/react-to-web-component.ts

+11-10
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import type { R2WCOptions } from "@r2wc/core"
2+
import type { Root } from "react-dom/client"
23

34
import React from "react"
4-
import ReactDOM from "react-dom"
5+
import { createRoot } from "react-dom/client"
56

67
import r2wcCore from "@r2wc/core"
78

89
interface Context<Props extends object> {
9-
container: HTMLElement
10+
root: Root
1011
ReactComponent: React.ComponentType<Props>
1112
}
1213

@@ -15,27 +16,27 @@ function mount<Props extends object>(
1516
ReactComponent: React.ComponentType<Props>,
1617
props: Props,
1718
): Context<Props> {
18-
const element = React.createElement(ReactComponent, props)
19+
const root = createRoot(container)
1920

20-
ReactDOM.render(element, container)
21+
const element = React.createElement(ReactComponent, props)
22+
root.render(element)
2123

2224
return {
23-
container,
25+
root,
2426
ReactComponent,
2527
}
2628
}
2729

2830
function update<Props extends object>(
29-
{ container, ReactComponent }: Context<Props>,
31+
{ root, ReactComponent }: Context<Props>,
3032
props: Props,
3133
): void {
3234
const element = React.createElement(ReactComponent, props)
33-
34-
ReactDOM.render(element, container)
35+
root.render(element)
3536
}
3637

37-
function unmount<Props extends object>({ container }: Context<Props>): void {
38-
ReactDOM.unmountComponentAtNode(container)
38+
function unmount<Props extends object>({ root }: Context<Props>): void {
39+
root.unmount()
3940
}
4041

4142
export default function r2wc<Props extends object>(

0 commit comments

Comments
 (0)