Add React Support #3

Merged
maxime.batista merged 12 commits from setup-typescript-react into master 1 year ago

As the editor and visualizer features of the project will require a significant amount of front-end work, we'll use the react framework and TypeScript to achieve our views.
This pull request adds TypeScript/React to the project, and a Continuous Deployment (CD) workflow to automatically deploy the website on our staging server (https://maxou.dev/IQBall/public) when the master branch gets new commits pushed.

Folder tree of the project

The Folder tree was updated, here are the goals of each directories.

.
├── ci            // CI/CD related directory. 
├── Documentation // Documentation directory.
├── front         // React code goes here.
│   ├── views     // React views goes here.
│   └── assets    // assets goes here (images, svg etc)
├── profiles      // PHP server environment profiles.
├── public        // index.php goes here
└── src           // php code goes here
    ├── Controller
    ├── Data
    └── ...

How does php sends react views to the client

As our views are now done using react (and defined under the front/views folder), we need to use the send_react_front($viewURI, $viewArguments) php function (located in the src/react-render.php file).
As there is nothing serious on the master branch regarding the project views, a sample controller (src/Controller/SampleFormController.php) with a form view (front/views/SampleForm.tsx) and a view that displays the information entered in the form (front/views/DisplayResult) has been made to let you understand how to use the send_react_front function to render the react views and how to bind data to the views.

React views conventions.

Conventions regarding our react views must be respected in order to be renderable.
If you look at the send_react_front($viewURI, $viewArguments) function, you'll see that is simply loads the file src/react-display-file.php with given arguments.
The file is a simple html5 template with a <script> block in the <body> section. The script block imports the requested view and will render it using a specific render(any) function. This function must figure in the imported view file in order to work.

The render(any) function

any react view file should export a function with signature render(arguments: any), which responsibility is to render the view.
The arguments parameter is used to pass data to the react component.

If you take a look at the front/views/SampleForm.tsx view, here's the definition of its render function :

/// Here's the definition of the view 
function SampleForm() {
	... react jsx code here
}

// the `SampleForm` does not inputs arguments but the parameter IS STILL REQUIRED
export function render(args: any) {
    const root = ReactDOM.createRoot(
        document.getElementById('root') as HTMLElement
    );
    root.render(
        <React.StrictMode>
            <SampleForm /> //here's our view component
        </React.StrictMode>
    );
}

Pass arguments to our react views

We'll take the example of the view in charge of displaying the SimpleForm's fields values.
First, let's take a look at its React Component definition :

function DisplayResults({password, username}: any) {
    return (
        <div>
            <p>username: {username}</p> //arguments are used heres
            <p>password: {password}</p>
        </div>
    )
}

The components takes two arguments, a username and password string.

Now, let's take a look at its render function :

export function render(args: any) {
    const root = ReactDOM.createRoot(
        document.getElementById('root') as HTMLElement
    );
    root.render(
        <React.StrictMode>
            <DisplayResults username={args.name} password={args.password} />
        </React.StrictMode>
    );
}

let's take a look at this line:

<DisplayResults username={args.name} password={args.password} />

DisplayResults is the name of our React Component (the function).
To pass arguments to the DisplayResults function, we use the syntax <function parameter name>={value}. (somewhat like regular html/xml components).
Use the render's function args parameter to access to the php's array values (see below).

So now we have defined our react view, let's send it to the user's browser from php.

To render the DisplayResults view, we'll use the send_react_front():

require_once "react-display.php";
...
send_react_front(
        "views/DisplayResults.tsx", // view file location, relative to the `/front` folder.
        array("name" => "samuel barion", "password" => "1234") //our function's arguments
)

NOTE: in the project, the $_POST is placed instead of the hardcoded array, as the $_POST is an array containing the "name" and "password" keys. (regarding the SampleForm's form inputs names)).

As the editor and visualizer features of the project will require a significant amount of front-end work, we'll use the react framework and TypeScript to achieve our views. This pull request adds TypeScript/React to the project, and a Continuous Deployment (CD) workflow to automatically deploy the website on our staging server (https://maxou.dev/IQBall/public) when the `master` branch gets new commits pushed. ## Folder tree of the project The Folder tree was updated, here are the goals of each directories. ```tree . ├── ci // CI/CD related directory. ├── Documentation // Documentation directory. ├── front // React code goes here. │ ├── views // React views goes here. │ └── assets // assets goes here (images, svg etc) ├── profiles // PHP server environment profiles. ├── public // index.php goes here └── src // php code goes here ├── Controller ├── Data └── ... ``` ### How does php sends react views to the client As our views are now done using react (and defined under the `front/views` folder), we need to use the `send_react_front($viewURI, $viewArguments)` php function (located in the `src/react-render.php` file). As there is nothing serious on the master branch regarding the project views, a sample controller (`src/Controller/SampleFormController.php`) with a form view (`front/views/SampleForm.tsx`) and a view that displays the information entered in the form (`front/views/DisplayResult`) has been made to let you understand how to use the `send_react_front` function to render the react views and how to bind data to the views. ### React views conventions. Conventions regarding our react views __must be respected in order to be renderable__. If you look at the `send_react_front($viewURI, $viewArguments)` function, you'll see that is simply loads the file `src/react-display-file.php` with given arguments. The file is a simple html5 template with a `<script>` block in the `<body>` section. The script block imports the requested view and will render it using a specific `render(any)` function. This function __must__ figure in the imported view file in order to work. #### The `render(any)` function __any react view file__ should __export__ a function with signature `render(arguments: any)`, which responsibility is to render the view. The `arguments` parameter is used to pass data to the react component. If you take a look at the `front/views/SampleForm.tsx` view, here's the definition of its render function : ```ts /// Here's the definition of the view function SampleForm() { ... react jsx code here } // the `SampleForm` does not inputs arguments but the parameter IS STILL REQUIRED export function render(args: any) { const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement ); root.render( <React.StrictMode> <SampleForm /> //here's our view component </React.StrictMode> ); } ``` ### Pass arguments to our react views We'll take the example of the view in charge of displaying the SimpleForm's fields values. First, let's take a look at its React Component definition : ```ts function DisplayResults({password, username}: any) { return ( <div> <p>username: {username}</p> //arguments are used heres <p>password: {password}</p> </div> ) } ``` The components takes two arguments, a `username` and `password` string. Now, let's take a look at its `render` function : ```ts export function render(args: any) { const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement ); root.render( <React.StrictMode> <DisplayResults username={args.name} password={args.password} /> </React.StrictMode> ); } ``` let's take a look at this line: ``` <DisplayResults username={args.name} password={args.password} /> ``` `DisplayResults` is the name of our React Component (the function). To pass arguments to the `DisplayResults` function, we use the syntax `<function parameter name>={value}`. (somewhat like regular html/xml components). Use the render's function `args` parameter to access to the php's array values (see below). So now we have defined our react view, let's send it to the user's browser from php. To render the `DisplayResults` view, we'll use the `send_react_front()`: ```php require_once "react-display.php"; ... send_react_front( "views/DisplayResults.tsx", // view file location, relative to the `/front` folder. array("name" => "samuel barion", "password" => "1234") //our function's arguments ) ``` NOTE: in the project, the `$_POST` is placed instead of the hardcoded array, as the `$_POST` is an array containing the "name" and "password" keys. (regarding the SampleForm's form inputs names)).
maxime.batista added the enhancement label 2 years ago
maxime.batista changed title from Add React Support to WIP: Add React Support 2 years ago
maxime.batista force-pushed setup-typescript-react from 7124a5ff06 to 6f0d82fb2b 2 years ago
maxime.batista force-pushed setup-typescript-react from 6f0d82fb2b to 7cd2e4603a 2 years ago
maxime.batista requested review from yanis.dahmane-bounoua 2 years ago
maxime.batista requested review from vivien.dufour 2 years ago
maxime.batista requested review from mael.daim 2 years ago
maxime.batista requested review from samuel.berion 2 years ago
maxime.batista changed title from WIP: Add React Support to Add React Support 2 years ago
maxime.batista force-pushed setup-typescript-react from bdc9bfc1cb to 106ef8ea1d 2 years ago
vivien.dufour approved these changes 2 years ago
maxime.batista force-pushed setup-typescript-react from 5e60438c48 to d08362afc4 1 year ago
maxime.batista force-pushed setup-typescript-react from 35ee995fef to 3de6743242 1 year ago
maxime.batista force-pushed setup-typescript-react from 3de6743242 to 88c5b326fe 1 year ago
maxime.batista force-pushed setup-typescript-react from 0a8edb0841 to e1f635c5dc 1 year ago
maxime.batista force-pushed setup-typescript-react from e1f635c5dc to 03873bd5f7 1 year ago
clement.freville2 requested changes 1 year ago
Conventions regarding our react views __must be respected in order to be renderable__.
### The `render(any)` function
__any react view file__ should __export__ a function with signature `render(arguments: any)`, which responsibility is to render the view.
- __any react view file__ should __export__ a function with signature `render(arguments: any)`, which responsibility is to render the view.  
+ Any React view component needs to be default exported in order to be imported and used from PHP. Those components will receive as props the arguments that the PHP server has transmitted.
```diff - __any react view file__ should __export__ a function with signature `render(arguments: any)`, which responsibility is to render the view. + Any React view component needs to be default exported in order to be imported and used from PHP. Those components will receive as props the arguments that the PHP server has transmitted. ```
maxime.batista marked this conversation as resolved
composer.json Outdated
"App\\": "src/"
}
},
"include-path": ["src"],
-   "include-path": ["src"],

It is deprecated.

```diff - "include-path": ["src"], ``` It is [deprecated](https://getcomposer.org/doc/04-schema.md#include-path).
maxime.batista marked this conversation as resolved
import ReactDOM from "react-dom/client";
import React, {FunctionComponent} from "react";
- import React, {FunctionComponent} from "react";
+ import type {FunctionComponent} from "react";
```diff - import React, {FunctionComponent} from "react"; + import type {FunctionComponent} from "react"; ```
maxime.batista marked this conversation as resolved
import ReactDOM from "react-dom/client";
import React from "react";
- import ReactDOM from "react-dom/client";
- import React from "react";

The React import can be omitted since React 17.

```diff - import ReactDOM from "react-dom/client"; - import React from "react"; ``` The React import can be omitted since [React 17](https://legacy.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html).
maxime.batista marked this conversation as resolved
import React from "react";
import ReactDOM from "react-dom/client";
- import React from "react";
- import ReactDOM from "react-dom/client";

The React import can be omitted since React 17.

```diff - import React from "react"; - import ReactDOM from "react-dom/client"; ``` The React import can be omitted since [React 17](https://legacy.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html).
maxime.batista marked this conversation as resolved
package.json Outdated
"@types/react-dom": "^18.2.14",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"typescript": "^4.9.5",
-     "typescript": "^4.9.5",
+     "typescript": "^5.2.2",
```diff - "typescript": "^4.9.5", + "typescript": "^5.2.2", ```
maxime.batista marked this conversation as resolved
package.json Outdated
"react-app/jest"
]
},
"browserslist": {
-   "browserslist": {
-     "production": [
-       ">0.2%",
-       "not dead",
-       "not op_mini all"
-     ],
-     "development": [
-       "last 1 chrome version",
-       "last 1 firefox version",
-       "last 1 safari version"
-     ]
-   },

browserslist is not read by Vite.

```diff - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - }, ``` `browserslist` is not read by Vite.
maxime.batista marked this conversation as resolved
public/index.php Outdated
/**
* relative path of the index.php's directory from the server's document root.
*/
global $basePath;
- global $basePath;

This is already a global variable since it is at the root scope.

```diff - global $basePath; ``` This is already a global variable since it is at the root scope.
maxime.batista marked this conversation as resolved
if ($match == null) {
// TODO redirect to a 404 not found page instead (issue #1)
echo "page non trouvée";
header('HTTP/1.1 404 Not Found');
-     echo "page non trouvée";
-     header('HTTP/1.1 404 Not Found');
+     http_response_code(404);
+     echo 'Page non trouvée';

Headers must be sent before any content.

```diff - echo "page non trouvée"; - header('HTTP/1.1 404 Not Found'); + http_response_code(404); + echo 'Page non trouvée'; ``` Headers must be sent before any content.
maxime.batista marked this conversation as resolved
}
?>
window.__vite_plugin_react_preamble_installed__ = true

This belongs to the SUPPORTS_FAST_REFRESH conditional.

This belongs to the `SUPPORTS_FAST_REFRESH` conditional.
maxime.batista marked this conversation as resolved
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body id="root">
- <body id="root">
+ <body>
+     <div id="root"></div>

Attaching React to the body is not recommended.

```diff - <body id="root"> + <body> + <div id="root"></div> ``` Attaching React to the body is not recommended.
clement.freville2 reviewed 1 year ago
maxime.batista force-pushed setup-typescript-react from c2bd07db78 to e7f5cc72c5 1 year ago
maxime.batista force-pushed setup-typescript-react from a20562dee7 to 9aea5bd80f 1 year ago
maxime.batista force-pushed setup-typescript-react from 9aea5bd80f to 7df33f32b5 1 year ago
maxime.batista merged commit 5fe901253c into master 1 year ago
maxime.batista deleted branch setup-typescript-react 1 year ago

Reviewers

samuel.berion was requested for review 2 years ago
yanis.dahmane-bounoua was requested for review 2 years ago
mael.daim was requested for review 2 years ago
vivien.dufour approved these changes 2 years ago
clement.freville2 requested changes 1 year ago
continuous-integration/drone/push Build is passing
The pull request has been merged as 5fe901253c.
Sign in to join this conversation.
No Milestone
No project
No Assignees
4 Participants
Notifications
Due Date

No due date set.

Dependencies

No dependencies set.

Reference: IQBall/Application-Web#3
Loading…
There is no content yet.