Skip to content

Commit 0a74f01

Browse files
crutchcorneps1lon
andauthored
Add more information about event and property binding on custom elements (#7939)
Co-authored-by: Sebastian "Sebbie" Silbermann <[email protected]>
1 parent e9a7cb1 commit 0a74f01

File tree

1 file changed

+124
-10
lines changed
  • src/content/reference/react-dom/components

1 file changed

+124
-10
lines changed

src/content/reference/react-dom/components/index.md

Lines changed: 124 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -162,23 +162,137 @@ Similar to the [DOM standard,](https://developer.mozilla.org/en-US/docs/Web/API/
162162

163163
### Custom HTML elements {/*custom-html-elements*/}
164164

165-
If you render a tag with a dash, like `<my-element>`, React will assume you want to render a [custom HTML element.](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) In React, rendering custom elements works differently from rendering built-in browser tags:
166-
167-
- All custom element props are serialized to strings and are always set using attributes.
168-
- Custom elements accept `class` rather than `className`, and `for` rather than `htmlFor`.
165+
If you render a tag with a dash, like `<my-element>`, React will assume you want to render a [custom HTML element.](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements)
169166

170167
If you render a built-in browser HTML element with an [`is`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/is) attribute, it will also be treated as a custom element.
171168

172-
<Note>
169+
#### Setting values on custom elements {/*attributes-vs-properties*/}
170+
171+
Custom elements have two methods of passing data into them:
172+
173+
1) Attributes: Which are displayed in markup and can only be set to string values
174+
2) Properties: Which are not displayed in markup and can be set to arbitrary JavaScript values
175+
176+
By default, React will pass values bound in JSX as attributes:
177+
178+
```jsx
179+
<my-element value="Hello, world!"></my-element>
180+
```
181+
182+
Non-string JavaScript values passed to custom elements will be serialized by default:
183+
184+
```jsx
185+
// Will be passed as `"1,2,3"` as the output of `[1,2,3].toString()`
186+
<my-element value={[1,2,3]}></my-element>
187+
```
188+
189+
React will, however, recognize an custom element's property as one that it may pass arbitrary values to if the property name shows up on the class during construction:
190+
191+
<Sandpack>
192+
193+
```js src/index.js hidden
194+
import {MyElement} from './MyElement.js';
195+
import { createRoot } from 'react-dom/client';
196+
import {App} from "./App.js";
197+
198+
customElements.define('my-element', MyElement);
199+
200+
const root = createRoot(document.getElementById('root'))
201+
root.render(<App />);
202+
```
203+
204+
```js src/MyElement.js active
205+
export class MyElement extends HTMLElement {
206+
constructor() {
207+
super();
208+
// The value here will be overwritten by React
209+
// when initialized as an element
210+
this.value = undefined;
211+
}
212+
213+
connectedCallback() {
214+
this.innerHTML = this.value.join(", ");
215+
}
216+
}
217+
```
173218

174-
[A future version of React will include more comprehensive support for custom elements.](https://github.com/facebook/react/issues/11347#issuecomment-1122275286)
219+
```js src/App.js
220+
export function App() {
221+
return <my-element value={[1,2,3]}></my-element>
222+
}
223+
```
175224

176-
You can try it by upgrading React packages to the most recent experimental version:
225+
</Sandpack>
226+
227+
#### Listening for events on custom elements {/*custom-element-events*/}
228+
229+
A common pattern when using custom elements is that they may dispatch [`CustomEvent`s](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) rather than accept a function to call when an event occur. You can listen for these events using an `on` prefix when binding to the event via JSX.
230+
231+
<Sandpack>
232+
233+
```js src/index.js hidden
234+
import {MyElement} from './MyElement.js';
235+
import { createRoot } from 'react-dom/client';
236+
import {App} from "./App.js";
237+
238+
customElements.define('my-element', MyElement);
239+
240+
const root = createRoot(document.getElementById('root'))
241+
root.render(<App />);
242+
```
243+
244+
```javascript src/MyElement.js
245+
export class MyElement extends HTMLElement {
246+
constructor() {
247+
super();
248+
this.test = undefined;
249+
this.emitEvent = this._emitEvent.bind(this);
250+
}
251+
252+
_emitEvent() {
253+
const event = new CustomEvent('speak', {
254+
detail: {
255+
message: 'Hello, world!',
256+
},
257+
});
258+
this.dispatchEvent(event);
259+
}
260+
261+
connectedCallback() {
262+
this.el = document.createElement('button');
263+
this.el.innerText = 'Say hi';
264+
this.el.addEventListener('click', this.emitEvent);
265+
this.appendChild(this.el);
266+
}
267+
268+
disconnectedCallback() {
269+
this.el.removeEventListener('click', this.emitEvent);
270+
}
271+
}
272+
```
273+
274+
```jsx src/App.js active
275+
export function App() {
276+
return (
277+
<my-element
278+
onspeak={e => console.log(e.detail.message)}
279+
></my-element>
280+
)
281+
}
282+
```
283+
284+
</Sandpack>
285+
286+
<Note>
177287

178-
- `react@experimental`
179-
- `react-dom@experimental`
288+
Events are case-sensitive and support dashes (`-`). Preserve the casing of the event and include all dashes when listening for custom element's events:
180289

181-
Experimental versions of React may contain bugs. Don't use them in production.
290+
```jsx
291+
// Listens for `say-hi` events
292+
<my-element onsay-hi={console.log}></my-element>
293+
// Listens for `sayHi` events
294+
<my-element onsayHi={console.log}></my-element>
295+
```
182296

183297
</Note>
184298
---

0 commit comments

Comments
 (0)