DEV Community

Rafael Cachoeira
Rafael Cachoeira

Posted on • Edited on

20 4

Invoke React components from data-attributes

Motivation

I have an ASPNET CORE MVC + jQuery application and I need to gradually migrate some components to React!

Since we already have the data from the ViewModel, I would like to pass it to the React component.

Proposal

Why not use data attributes?
I created a data attribute structure that can be read in React and invoke my components (Lazy) with their properties.
That way, I don't need to write javascript code every time to bind 'react' to html.

Requisites

  • Webpack
  • Webpack chunk
  • React

My structure of data-attributes

  <div data-js-component="FavoriteButton"
       data-js-props='{
          "favorite": @Model.Document.Favorite.ToString().ToLower()
      }'>
  </div>
Enter fullscreen mode Exit fullscreen mode
  • data-js-component: string (name of component to scan and invoke)
  • data-js-props: json (all properties of initial state)

My React Component

import React from 'react';

export default function FavoriteButton({ favorite }) {
  ...
  ...
}

Enter fullscreen mode Exit fullscreen mode

My InvokeComponents:

How to Works

First, register your components with their respective path to lazy import on 'components' object.
It will be searched for [data-js-component] in the html. When the element is found, it will be read from the 'components' object.
The [data-js-props] will be cast to json and pass to React Component found.


import { lazy, Suspense } from 'react';
import ReactDOM from 'react-dom';

const InvokeComponents = (function() {

    //register here your components
    const components = {
        DocumentFavoriteButton: lazy(() => import('./Documents/DocumentFavoriteButton')),
        FavoriteButton: lazy(() => import('./FavoriteButton'))
    }

    const elements = document.querySelectorAll('[data-js-component]');

    for (const element of elements) {
        const { jsComponent, jsProps } = element.dataset;

        const ComponentFound = components[jsComponent];
        let props = JSON.parse(jsProps);

        ReactDOM.render(
            <Suspense fallback={<p>...</p>}>
                <ComponentFound {...props} />
            </Suspense>,
            element
        );

    }
})();

export default InvokeComponents;

Enter fullscreen mode Exit fullscreen mode

Now, register your InvokeComponent on _layout cshtml page:

<script src="/dist/Components/InvokeComponents.js" asp-append-version="true"></script>
Enter fullscreen mode Exit fullscreen mode

And finishing, modify your webpack.config like this to support chunk used on lazy.

  output: {
        path: path.resolve(__dirname, 'wwwroot'),
        publicPath: '/',
        chunkFilename: '[hash].[name].js',
        filename: '[name]'
    },

Enter fullscreen mode Exit fullscreen mode

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (2)

Collapse
 
brunot profile image
Bruno

Instead of using React take a look at Preact + Preact Habitat. It is a really nice combination to achieve what you are trying to do. You will end up with simple widgets you can invoke and have a very small size because of Preact. Later, if you really need, you can easily replace Preact with React and get rid of Preact-habitat.

Collapse
 
thevarunraja profile image
Varun Raja

Very cool approach. Thanks for sharing.

Cloudinary image

Optimize, customize, deliver, manage and analyze your images.

Remove background in all your web images at the same time, use outpainting to expand images with matching content, remove objects via open-set object detection and fill, recolor, crop, resize... Discover these and hundreds more ways to manage your web images and videos on a scale.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay