Symfony ux-react: React component does not render when using react_component()

1.3k views Asked by At

I've initialized a new Symfony project with version 6.3.3 and wanted to try out the symfony-ux stuff with React. So I followed this guide: https://symfony.com/bundles/ux-react/current/index.html But I cannot get the react component to render on the page.

I have installed all the packages, and have a structure looking like this: enter image description here

Then my controllers.json looks like this:

{
    "controllers": {
        "@symfony/ux-react": {
            "react": {
                "enabled": true,
                "fetch": "eager"
            }
        }
    },
    "entrypoints": []
}

My bootstrap.js looks like this:

import { startStimulusApp } from '@symfony/stimulus-bridge';
// Registers Stimulus controllers from controllers.json and in the controllers/ directory
export const app = startStimulusApp(require.context(
    '@symfony/stimulus-bridge/lazy-controller-loader!./controllers',
    true,
    /\.[jt]sx?$/
));
// register any custom, 3rd party controllers here
// app.register('some_controller_name', SomeImportedController);

app.js looks like this:

import { registerReactControllerComponents } from '@symfony/ux-react';
import './bootstrap.js';
/*
 * Welcome to your app's main JavaScript file!
 *
 * We recommend including the built version of this JavaScript file
 * (and its CSS file) in your base layout (base.html.twig).
 */

// any CSS you import will output into a single css file (app.css in this case)
import './styles/app.css';

registerReactControllerComponents(require.context('./react/controllers', true, /\.(j|t)sx?$/));

Hello.jsx looks like this:

import React from 'react';

export default function (props) {

   return <div>TESTING</div>;
}

And in my home.html.twig I've got the following:

{% extends 'base.html.twig' %}

{% block body %}
    <div>
        Component should render below
        <div {{ react_component('Hello') }}>
          
        </div>
    </div>
{% endblock %}

And I get no errors in the logs, so everything seem to be fine with the modules installed. But the Hello.jsx react component doesn't show. Only the other stuff that I've written in the template.

However, when inspecting I can see that stimulus have put this content into the div opening tag:

data-controller="symfony--ux-react--react" data-symfony--ux-react--react-component-value="Hello"

But the TEST-text inside just won't render on the page. If someone could help me take a look at this, I'd be greatful :)

My package.json looks like this by the way:

{
    "devDependencies": {
        "@babel/core": "^7.17.0",
        "@babel/preset-env": "^7.16.0",
        "@babel/preset-react": "^7.22.5",
        "@hotwired/stimulus": "^3.0.0",
        "@symfony/stimulus-bridge": "^3.2.0",
        "@symfony/ux-react": "file:vendor/symfony/ux-react/assets",
        "@symfony/webpack-encore": "^4.0.0",
        "core-js": "^3.23.0",
        "react": "^18.2.0",
        "react-dom": "^18.0",
        "regenerator-runtime": "^0.13.9",
        "webpack": "^5.74.0",
        "webpack-cli": "^4.10.0",
        "webpack-notifier": "^1.15.0"
    },
    "license": "UNLICENSED",
    "private": true,
    "scripts": {
        "dev-server": "encore dev-server",
        "dev": "encore dev",
        "watch": "encore dev --watch",
        "build": "encore production --progress"
    }
}

EDIT Here is how the webpack.config looks:

const Encore = require('@symfony/webpack-encore');

// Manually configure the runtime environment if not already configured yet by the "encore" command.
// It's useful when you use tools that rely on webpack.config.js file.
if (!Encore.isRuntimeEnvironmentConfigured()) {
    Encore.configureRuntimeEnvironment(process.env.NODE_ENV || 'dev');
}

Encore
    // directory where compiled assets will be stored
    .setOutputPath('public/build/')
    // public path used by the web server to access the output path
    .setPublicPath('/build')
    // only needed for CDN's or subdirectory deploy
    //.setManifestKeyPrefix('build/')

    /*
     * ENTRY CONFIG
     *
     * Each entry will result in one JavaScript file (e.g. app.js)
     * and one CSS file (e.g. app.css) if your JavaScript imports CSS.
     */
    .addEntry('app', './assets/app.js')

    // When enabled, Webpack "splits" your files into smaller pieces for greater optimization.
    .splitEntryChunks()

    // enables the Symfony UX Stimulus bridge (used in assets/bootstrap.js)
    .enableStimulusBridge('./assets/controllers.json')

    // will require an extra script tag for runtime.js
    // but, you probably want this, unless you're building a single-page app
    .enableSingleRuntimeChunk()

    /*
     * FEATURE CONFIG
     *
     * Enable & configure other features below. For a full
     * list of features, see:
     * https://symfony.com/doc/current/frontend.html#adding-more-features
     */
    .cleanupOutputBeforeBuild()
    .enableBuildNotifications()
    .enableSourceMaps(!Encore.isProduction())
    // enables hashed filenames (e.g. app.abc123.css)
    .enableVersioning(Encore.isProduction())

    // configure Babel
    // .configureBabel((config) => {
    //     config.plugins.push('@babel/a-babel-plugin');
    // })

    // enables and configure @babel/preset-env polyfills
    .configureBabelPresetEnv((config) => {
        config.useBuiltIns = 'usage';
        config.corejs = '3.23';
    })

    // enables Sass/SCSS support
    //.enableSassLoader()

    // uncomment if you use TypeScript
    //.enableTypeScriptLoader()

    // uncomment if you use React
    .enableReactPreset()

    // uncomment to get integrity="..." attributes on your script & link tags
    // requires WebpackEncoreBundle 1.4 or higher
    //.enableIntegrityHashes(Encore.isProduction())

    // uncomment if you're having problems with a jQuery plugin
    //.autoProvidejQuery()
;

module.exports = Encore.getWebpackConfig();

And the base.html.twig:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>{% block title %}Welcome!{% endblock %}</title>
        <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text></svg>">
        {% block stylesheets %}
        {% endblock %}

        {% block javascripts %}
        {% endblock %}
    </head>
    <body>
        {% block body %}{% endblock %}
    </body>
</html>
1

There are 1 answers

2
Lissam On BEST ANSWER

have you compiled your .jsx using assets mapper ? I can't see the compiled .js file in your structure.

In the link, https://symfony.com/bundles/ux-react/current/index.html#using-with-assetmapper it is said :

Because the JSX format isn't pure JavaScript, using this library with AssetMapper requires some extra steps.

Compile your .jsx files to pure JavaScript files. This can be done by installing Babel and the @babel/preset-react preset.

Moreover, i can see that you are using webpack-encore in your dependencies. Can we see your webpack.config.js and your base.html.twig ? There should be this line in the later that load your script :

{{ encore_entry_script_tags('app') }}

Thanks in advance.

(i should have put this in comment put i don't have 50rep for now :/ )

Edit : According to the webpack encore documentation, your base.html.twig must contain

{% block javascripts %}
    {{ encore_entry_script_tags('app') }}
{% endblock %}

to load the javascript files. Then you need to register the new .js files into webpack.config.js like this :

https://symfony.com/doc/current/frontend/encore/simple-example.html#multiple-entries

And import the .js file that you need in the twig in need (here your react file into the twig where your need it by using ineritance). With this, your app.js file should be executed.

To compile your .js files, you have to use the command :

npm run watch

To load the CSS it's this code in base.html.twig :

{% block stylesheets %}
    {{ encore_entry_link_tags('app') }}
{% endblock %}