Skip to content

Commit 4059895

Browse files
committed
Fix reviews
1 parent c52e713 commit 4059895

File tree

7 files changed

+118
-106
lines changed

7 files changed

+118
-106
lines changed

src/React/DependencyInjection/ReactExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
use Symfony\UX\React\Twig\ReactComponentExtension;
1919

2020
/**
21-
* @author Titouan Galopin <galopintitouan@gm ail.com>
21+
* @author Titouan Galopin <galopintitouan@gmail.com>
2222
*
2323
* @internal
2424
*/

src/React/README.md

Lines changed: 2 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,11 @@
11
# Symfony UX React
22

3-
Symfony UX React integrates [React](https://reactjs.org/) into Symfony applications. It provides tools
4-
to render React components from Twig.
3+
Symfony UX React integrates [React](https://reactjs.org/) into Symfony applications.
4+
It provides tools to render React components from Twig.
55

66
**This repository is a READ-ONLY sub-tree split**. See
77
https://github.com/symfony/ux to create issues or submit pull requests.
88

9-
## Usage
10-
11-
```bash
12-
composer require symfony/ux-react
13-
14-
# Don't forget to install the JavaScript dependencies as well and compile
15-
$ yarn install --force
16-
$ yarn encore dev
17-
```
18-
19-
Then in your `assets/app.js` file, add the following lines:
20-
21-
```
22-
// assets/app.js
23-
import { registerReactControllerComponents } from '@symfony/ux-react';
24-
25-
// Registers React controller components to allow loading them from Twig
26-
//
27-
// React controller components are components that are meant to be rendered
28-
// from Twig. These component then rely on other components that won't be called
29-
// directly from Twig.
30-
//
31-
// By putting only controller components in `react/controllers`, you ensure that
32-
// internal components won't be automatically included in your JS built file if
33-
// they are not necessary.
34-
registerReactControllerComponents(require.context('./react/controllers', true, /\.(j|t)sx?$/));
35-
```
36-
37-
You can now create your first React controller component in `assets/react/controllers`:
38-
39-
```js
40-
// assets/react/controllers/MyComponent.jsx
41-
import React from 'react';
42-
43-
export default function (props) {
44-
return <div>Hello {props.fullName}</div>;
45-
}
46-
```
47-
48-
And use it in your Twig files:
49-
50-
```twig
51-
{# templates/home.html.twig #}
52-
53-
<div {{ react_component('MyComponent', { 'fullName': app.user.fullName }) }}></div>
54-
```
55-
569
## Resources
5710

5811
- [Documentation](https://symfony.com/bundles/ux-react/current/index.html)

src/React/Resources/assets/dist/render_controller.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@ import { Controller } from '@hotwired/stimulus';
44
class default_1 extends Controller {
55
connect() {
66
this._dispatchEvent('react:connect', { component: this.componentValue, props: this.propsValue });
7-
if (this.element.timeout) {
8-
clearTimeout(this.element.timeout);
9-
}
10-
this.element.timeout = setTimeout(() => {
11-
const component = window.resolveReactComponent(this.componentValue);
12-
this._renderReactElement(React.createElement(component, this.propsValue, null));
13-
this._dispatchEvent('react:mount', { component: this.componentValue, props: this.propsValue });
14-
}, 50);
7+
const component = window.resolveReactComponent(this.componentValue);
8+
this._renderReactElement(React.createElement(component, this.propsValue, null));
9+
this._dispatchEvent('react:mount', {
10+
componentName: this.componentValue,
11+
component: component,
12+
props: this.propsValue,
13+
});
1514
}
1615
disconnect() {
1716
this.element.unmount();
@@ -33,7 +32,7 @@ class default_1 extends Controller {
3332
};
3433
}
3534
_dispatchEvent(name, payload) {
36-
this.element.dispatchEvent(new CustomEvent(name, { detail: payload }));
35+
this.element.dispatchEvent(new CustomEvent(name, { detail: payload, bubbles: true }));
3736
}
3837
}
3938
default_1.values = {

src/React/Resources/assets/package.json

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,15 @@
1616
},
1717
"peerDependencies": {
1818
"@hotwired/stimulus": "^3.0.0",
19-
"react": "^16.0 || ^17.0 || ^18.0",
20-
"react-dom": "^16.0 || ^17.0 || ^18.0"
21-
},
22-
"dependencies": {
23-
"@types/webpack-env": "^1.16"
19+
"react": "^18.0",
20+
"react-dom": "^18.0"
2421
},
2522
"devDependencies": {
2623
"@hotwired/stimulus": "^3.0.0",
27-
"@types/react": "^16.0 || ^17.0 || ^18.0",
28-
"react": "^16.0 || ^17.0 || ^18.0",
29-
"react-dom": "^16.0 || ^17.0 || ^18.0"
24+
"@types/react": "^18.0",
25+
"@types/react-dom": "^18.0",
26+
"@types/webpack-env": "^1.16",
27+
"react": "^18.0",
28+
"react-dom": "^18.0"
3029
}
3130
}

src/React/Resources/assets/src/register_controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export function registerReactControllerComponents(context: __WebpackModuleApi.Re
2020

2121
// Expose a global React loader to allow rendering from the Stimulus controller
2222
(window as any).resolveReactComponent = (name: string): object => {
23-
const component = reactControllers['./' + name + '.jsx'] || reactControllers['./' + name + '.tsx'];
23+
const component = reactControllers[`./${name}.jsx`] || reactControllers[`./${name}.tsx`];
2424
if (typeof component === 'undefined') {
2525
throw new Error('React controller "' + name + '" does not exist');
2626
}

src/React/Resources/assets/src/render_controller.ts

Lines changed: 12 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
'use strict';
1111

1212
import React, { ReactElement } from 'react';
13+
import { createRoot } from 'react-dom/client';
1314
import { Controller } from '@hotwired/stimulus';
1415

1516
export default class extends Controller {
@@ -24,52 +25,26 @@ export default class extends Controller {
2425
connect() {
2526
this._dispatchEvent('react:connect', { component: this.componentValue, props: this.propsValue });
2627

27-
// Use a timeout to avoid mounting and demounting right after
28-
if ((this.element as any).timeout) {
29-
clearTimeout((this.element as any).timeout);
30-
}
28+
const component = window.resolveReactComponent(this.componentValue);
29+
this._renderReactElement(React.createElement(component, this.propsValue, null));
3130

32-
(this.element as any).timeout = setTimeout(() => {
33-
const component = window.resolveReactComponent(this.componentValue);
34-
this._renderReactElement(React.createElement(component, this.propsValue, null));
35-
36-
this._dispatchEvent('react:mount', {
37-
componentName: this.componentValue,
38-
component: component,
39-
props: this.propsValue,
40-
});
41-
}, 50);
31+
this._dispatchEvent('react:mount', {
32+
componentName: this.componentValue,
33+
component: component,
34+
props: this.propsValue,
35+
});
4236
}
4337

4438
disconnect() {
45-
(this.element as any).unmount();
39+
(this.element as any).root.unmount();
4640
this._dispatchEvent('react:unmount', { component: this.componentValue, props: this.propsValue });
4741
}
4842

4943
_renderReactElement(reactElement: ReactElement) {
50-
if (parseInt(React.version) >= 18) {
51-
// React 18+ rendering
52-
// eslint-disable-next-line @typescript-eslint/no-var-requires
53-
const root = require('react-dom/client').createRoot(this.element);
54-
root.render(reactElement);
55-
56-
// Register a way to unmount this element
57-
(this.element as any).unmount = () => {
58-
root.unmount();
59-
};
60-
61-
return;
62-
}
63-
64-
// Up to React 17 rendering
65-
// eslint-disable-next-line @typescript-eslint/no-var-requires
66-
const reactDom = require('react-dom');
67-
reactDom.render(reactElement, this.element);
44+
const root = createRoot(this.element);
45+
root.render(reactElement);
6846

69-
// Register a way to unmount this element
70-
(this.element as any).unmount = () => {
71-
reactDom.unmountComponentAtNode(this.element);
72-
};
47+
(this.element as any).root = root;
7348
}
7449

7550
_dispatchEvent(name: string, payload: any) {

src/React/Resources/doc/index.rst

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,90 @@
11
Symfony UX React
22
================
33

4-
TODO
4+
Symfony UX React is a Symfony bundle integrating `React`_ in
5+
Symfony applications. It is part of `the Symfony UX initiative`_.
6+
7+
React is a JavaScript library for building user interfaces.
8+
Symfony UX React provides tools to render React components from Twig,
9+
handling rendering and data transfers.
10+
11+
Symfony UX React supports React 18+.
12+
13+
Installation
14+
------------
15+
16+
Before you start, make sure you have `Symfony UX configured in your app`_.
17+
18+
Then install the bundle using Composer and Symfony Flex:
19+
20+
.. code-block:: terminal
21+
22+
$ composer require symfony/ux-react
23+
24+
# Don't forget to install the JavaScript dependencies as well and compile
25+
$ yarn install --force
26+
$ yarn encore dev
27+
28+
You also need to add the following lines at the end to your ``assets/app.js`` file:
29+
30+
.. code-block:: javascript
31+
32+
// assets/app.js
33+
import { registerReactControllerComponents } from '@symfony/ux-react';
34+
35+
// Registers React controller components to allow loading them from Twig
36+
//
37+
// React controller components are components that are meant to be rendered
38+
// from Twig. These component then rely on other components that won't be called
39+
// directly from Twig.
40+
//
41+
// By putting only controller components in `react/controllers`, you ensure that
42+
// internal components won't be automatically included in your JS built file if
43+
// they are not necessary.
44+
registerReactControllerComponents(require.context('./react/controllers', true, /\.(j|t)sx?$/));
45+
46+
47+
Usage
48+
-----
49+
50+
UX React works by using a system of **React controller components**: React components that
51+
are registered using ``registerReactControllerComponents`` and that are meant to be rendered
52+
from Twig.
53+
54+
When using the ``registerReactControllerComponents`` configuration shown previously, all
55+
React components located in the directory ``assets/react/controllers`` are registered as
56+
React controller components.
57+
58+
You can then render any React controller component in Twig using the ``react_component``.
59+
For example:
60+
61+
.. code-block:: javascript
62+
63+
// assets/react/controllers/MyComponent.jsx
64+
import React from 'react';
65+
66+
export default function (props) {
67+
return <div>Hello {props.fullName}</div>;
68+
}
69+
70+
71+
.. code-block:: twig
72+
73+
{# templates/home.html.twig #}
74+
75+
<div {{ react_component('MyComponent', { 'fullName': app.user.fullName }) }}></div>
76+
77+
Backward Compatibility promise
78+
------------------------------
79+
80+
This bundle aims at following the same Backward Compatibility promise as
81+
the Symfony framework:
82+
https://symfony.com/doc/current/contributing/code/bc.html
83+
84+
However it is currently considered `experimental`_,
85+
meaning it is not bound to Symfony's BC policy for the moment.
86+
87+
.. _`React`: https://reactjs.org/
88+
.. _`the Symfony UX initiative`: https://symfony.com/ux
89+
.. _`experimental`: https://symfony.com/doc/current/contributing/code/experimental.html
90+
.. _`Symfony UX configured in your app`: https://symfony.com/doc/current/frontend/ux.html

0 commit comments

Comments
 (0)