React dans Symfony avec Vite
Tags: react, symfony, vite
Published: 2023-02-11 21:19:36

Problème du jour : je veux me mettre à React, mais avec un backend Symfony.

Alors OK, vous allez me dire d'utiliser Webpack Encore et vous avez raison.... sauf que webpack est lent, surtout dans la phase de dev.

Donc l'idée est de trouver comment intégrer Vite à notre projet et comment configurer Vite pour faire marcher React.

## Créons notre projet Symfony (6)

Rien de bien compliqué ici avec la CLI Symfony:

`symfony new --webapp reactSymfonyProject`

## Intégrer Vite à mon projet Symfony

Heureusement, quelqu'un s'est penché sur le problème pour nous (même si le faire à la main n'aurait pas non plus été infaisable). Il y a donc un bundle pour ça !

Entrez dans le répertoire du projet et installez le bundle:

`composer require pentatrion/vite-bundle`

Voici le résultat de l'installation :


![resultat installation du bundle](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k89wjqyz7x79oljwkx12.png)

On va donc faire comme indiqué, en sautant la première étape - en bref on laisse les paramètres par défaut.
Si la deuxième vaut quand même le coup d'oeil (allez jeter un oeil au fichier vite.config.js comme indiqué), le fichier ne requiert aucune modification.  

Suivez l'étape 3 et installez les dépendances Javascripts

`yarn install (ou npm install)`

Enfin, on va aller modifier notre template Twig de base pour aller chercher le résultat de la compilation de Vite.

Dans le fichier templates/base.html.twig, remplacez la ligne :
`{{ encore_entry_link_tags('app') }} `
par
`{{ vite_entry_link_tags('app') }}`

puis la ligne:
`{{ encore_entry_script_tags('app') }}`
par 
`{{ vite_entry_script_tags('app', { dependency: 'react' }) }}`

## Création d'un contrôleur de test

A la racine du projet :

`symfony console make:controller Test`
 
On a maintenant une URL /test de disponible

## Premier test
On va démarrer tout ça et voir le résultat

D'abord on va démarrer le serveur PHP interne:
`symfony serve -d`

Puis on va compiler nos assets:
`yarn dev`

Rendez-vous maintenant dans votre navigateur à l'adresse :
[https://127.0.0.1:8000/test](https://127.0.0.1:8000/test)

Note : le port 8000 peut différer sur votre installation. L'adresse est indiquée quand vous démarrez le serveur PHP.

Maintenant constatez le résultat en ouvrant la console Javascript (F12):

![visualisation du résultat dans le navigateur](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v1f7ft8eibppo43q4ea0.png)

On voit ici qu'on retrouve bien le résultat de notre script assets/app.js et que Vite est bien démarré en mode dev.

Vous pouvez faire un test pour voir la puissance de Vite au regard de Webpack.

Ajouter à la fin du fichier assets/app.js n'importe quel `console.log('plop')`, et regardez le résultat apparaître dans la console instantanément, sans recharger la page du navigateur ! 

## OK pour Vite, mais pour React ?

Pour l'instant on est d'accord, Vite marche mais pas de React en vue (notez la blague React - Vue).

Il nous manque 2 choses : du code certes, mais aussi des dépendances.

Installons-les:

```sh
yarn add -D @vitejs/plugin-react @types/react @types/react-dom
yarn add react react-dom

```

On va maintenant transformer notre Javascript et faire un peu de React.

Ouvrez le fichier assets/app.js et transformez-le comme suit:

```jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'

import "./app.css";

ReactDOM.createRoot(document.getElementById('root')).render(
    <React.StrictMode>
        <App />
    </React.StrictMode>,
)
  
```
Comme vous le voyez, on va "rendre" notre application dans un element avec un ID "root".

On ne l'a pas pour le moment. Pour cela, on va modifier notre page de test (fichier templates/test/index.html.twig) comme suit:
```html
{% extends 'base.html.twig' %}

{% block title %}Hello TestController!{% endblock %}

{% block body %}
<div id="root"></div>
{% endblock %}
```
On va relancer Vite, et voir ce que ça donne....
A la racine du projet, relancez Vite;
`yarn dev`

Ouch, une grosse erreur apparaît et c'est bien normal :

![Erreur de compilation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/puuux0czlgw2ih4galqm.png)

On a mis du JSX dans un fichier avec l'extension ".js".
Pour des raisons pratiques, on va renommer notre app.js en main.jsx

N'oublions pas non plus de modifier notre vite.config.js en pointant aussi vers le fichier main.jsx au lieu de app.js. On va aussi en profiter pour dire à Vite qu'on utilise son plugin React. Votre fichier vite.config.js doit donc ressembler à ça:

```js
import { defineConfig } from "vite";
import symfonyPlugin from "vite-plugin-symfony";
import react from '@vitejs/plugin-react'


export default defineConfig({
    plugins: [
        react(),
        symfonyPlugin(),
    ],
    build: {
        rollupOptions: {
            input: {
                app: "./assets/main.jsx"
            },
        }
    },
});

```

Relancez Vite, et constatez que vous avez une nouvelle erreur....normal, on a pas fini notre code ;)
On dit dans notre fichier main.jsx de rendre le composant "App" mais on ne l'a pas créé ! C'est aussi pour des raisons de conflit qu'on a renommé notre app.js en main.jsx et non en app.jsx.

Créez maintenant au même niveau que main.jsx un fichier App.jsx avec ce contenu :

```jsx
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from './images/vite.svg'

function App() {
  const [count, setCount] = useState(0)

  return (
    <div className="App">
      <div>
        <a href="https://vitejs.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://reactjs.org" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          Edit <code>assets/App.jsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
    </div>
  )
}

export default App

```

Note: vous trouverez les logos de React et Vite en SVG sur Internet ;)

Modifiez aussi le CSS de l'application (fichier assets/app.css) comme suit:

```css
#root {
  max-width: 1280px;
  margin: 0 auto;
  padding: 2rem;
  text-align: center;
}

.logo {
  height: 6em;
  padding: 1.5em;
  will-change: filter;
  transition: filter 300ms;
}
.logo:hover {
  filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
  filter: drop-shadow(0 0 2em #61dafbaa);
}

@keyframes logo-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

@media (prefers-reduced-motion: no-preference) {
  a:nth-of-type(2) .logo {
    animation: logo-spin infinite 20s linear;
  }
}

.card {
  padding: 2em;
}

.read-the-docs {
  color: #888;
}

```

Et voilà, normalement, vous devriez avoir cette page qui apparait dans votre navigateur :


![Capture résultat final](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r3f28554fe3lpcxbqkbh.png)