simplify react view displaying from php, bring back auto/fast refresh

pull/3/head
maxime.batista 2 years ago
parent 594d57fe05
commit 64803f8ac2

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 48 KiB

@ -62,20 +62,19 @@ As our views are now done using react (and defined under the `front/views` folde
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 script block imports the requested view and will render it.
The view entry is a function, named in PascalCase, which __must__ be be exported by default (`export default function MyReactView(args: {..})`).
```html
<!--
here's the magic.
imports the given view URL, and assume that the view exports a function named `render` that
takes exactly one object argument.
If the imported file does not export a function with signature `render(arguments: any)`,
the call wont work thus the view will not be displayed
imports the given view URL, and assume that the view exports a function named `Component`.
see ViewRenderer.tsx::renderView for more info
-->
<script type="module">
import {render} from "<?= asset($url) ?>"
render(<?= json_encode($arguments) ?>)
import {renderView} from "<?= asset("ViewRenderer.tsx") ?>"
import Component from "<?= asset($url) ?>"
renderView(Component, <?= json_encode($arguments) ?>)
</script>
```
@ -84,11 +83,8 @@ here's how it renders if you do a request to `http://localhost:8080/`.
![](assets/render-react-php-file-processed.png)
The index.php's router says that for a `GET` on the `/` url, we call the `SampleFormController#displayForm` method.
This method then uses the `send_react_front`, to render the `views/SampleForm.tsx` react element, with no arguments (an empty array).
You can see that the react view is rendered using a `render` function.
This function **must figure in the view's file, and be exported**.
We'll talk about an important coding convention with react views that this script block requires later.
The view file **must export by default its react function component**.
But now let's talk about our server profiles !
## Server Profiles
If you go on the staging server, you'll see that, for the exact same request equivalent, the generated `src/render-display-file` file changes :
![](assets/staging-server-render-react-php-file-processed.png)
@ -107,11 +103,15 @@ the file is replaced with `prod-config-file.php` by the CI when deploying to the
The two profiles declares an `_asset(string $uri)` function, used by the `/config.php::asset` method, but with different implementations :
### Development profile
```php
const FRONT_URL_CONSTANT = "http://localhost:5173";
$hostname = getHostName();
$front_url = "http://$hostname:5173";
function _asset(string $assetURI): string {
return FRONT_URL_CONSTANT . "/" . $assetURI;
global $front_url;
return $front_url . "/" . $assetURI;
}
```
The simplest profile, simply redirect all assets to the development server
@ -148,60 +148,3 @@ __any react view file__ should __export__ a function with signature `render(argu
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, an `username` and a `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>
);
}
```
especially 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.

@ -5,6 +5,8 @@
// Please do not touch.
require /*PROFILE_FILE*/ "profiles/dev-config-profile.php";
CONST SUPPORTS_FAST_REFRESH = _SUPPORTS_FAST_REFRESH;
/**
* Maps the given relative source uri (relative to the `/front` folder) to its actual location depending on imported profile.
* @param string $assetURI relative uri path from `/front` folder
@ -14,3 +16,4 @@ function asset(string $assetURI): string {
return _asset($assetURI);
}

@ -0,0 +1,19 @@
import ReactDOM from "react-dom/client";
import React, {FunctionComponent} from "react";
/**
* Dynamically renders a React component, with given arguments
* @param Component the react component to render
* @param args the arguments to pass to the react component.
*/
export function renderView(Component: FunctionComponent, args: {}) {
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<Component {...args}/>
</React.StrictMode>
);
}

@ -2,7 +2,7 @@ import ReactDOM from "react-dom/client";
import React from "react";
function DisplayResults({password, username}: any) {
export default function DisplayResults({password, username}: any) {
return (
<div>
<p>username: {username}</p>
@ -10,15 +10,3 @@ function DisplayResults({password, username}: any) {
</div>
)
}
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>
);
}

@ -1,13 +1,13 @@
import React from "react";
import ReactDOM from "react-dom/client";
function SampleForm() {
export default function SampleForm() {
return (
<div>
<h1>Hello, this is a sample form made in react !</h1>
<form action="result" method="POST">
<label>your name: </label>
<input type="text" id="name" name="name"/>
<input type="text" id="name" name="username"/>
<label>your password: </label>
<input type="password" id="password" name="password"/>
<input type="submit" value="click me!"/>
@ -16,15 +16,5 @@ function SampleForm() {
)
}
export function render(args: any) {
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<SampleForm />
</React.StrictMode>
);
}

@ -1,8 +1,12 @@
<?php
const FRONT_URL_CONSTANT = "http://localhost:5173";
$hostname = getHostName();
$front_url = "http://$hostname:5173";
const _SUPPORTS_FAST_REFRESH = true;
function _asset(string $assetURI): string {
return FRONT_URL_CONSTANT . "/" . $assetURI;
global $front_url;
return $front_url . "/" . $assetURI;
}

@ -4,6 +4,9 @@
// in an `ASSETS` array constant.
require "../views-mappings.php";
const _SUPPORTS_FAST_REFRESH = false;
function _asset(string $assetURI): string {
// use index.php's base path
global $basePath;

@ -2,6 +2,18 @@
<html lang="en">
<head>
<script type="module">
<?php
if (SUPPORTS_FAST_REFRESH) {
$asset_server = asset("");
echo "
import RefreshRuntime from '{$asset_server}front/@react-refresh'
RefreshRuntime.injectIntoGlobalHook(window)
window.\$RefreshReg$ = () => {}
window.\$RefreshSig$ = () => (type) => type
";
}
?>
window.__vite_plugin_react_preamble_installed__ = true
</script>
@ -16,14 +28,13 @@
<!--
here's the magic.
imports the given view URL, and assume that the view exports a function named `render` that
takes exactly one object argument.
If the imported file does not export a function with signature `render(arguments: any)`,
the call wont work thus the view will not be displayed
imports the given view URL, and assume that the view exports a function named `Component`.
see ViewRenderer.tsx::renderView for more info
-->
<script type="module">
import {render} from "<?= asset($url) ?>"
render(<?= json_encode($arguments) ?>)
import {renderView} from "<?= asset("ViewRenderer.tsx") ?>"
import Component from "<?= asset($url) ?>"
renderView(Component, <?= json_encode($arguments) ?>)
</script>
<script>

Loading…
Cancel
Save