question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Feature Idea - Component-API

See original GitHub issue

Feature Idea - Stimulus Component API

Executive Summary

Design an API to support mounting of Javascript-Components for very interactive parts of the application WITHOUT compromising the features of Turbo. There’s a demo for vue, react and svelte adapters I built: https://stimulus-component-api.konow.ski/

Introduction

Hotwire allows to implement an SPAish user-experience while only writing server-side rendered HTML, which is cool and efficient. But sometimes I struggle with parts of the UI that are just SUPER-interactive and require way too much custom JS for my taste.

Some examples are:

  • maps
    • how to structure the code so it doesn’t look like my kitchen when I cook spaghetti?
  • multi-step wizards - optionally with alternative routes
    • Where to put the state? Put it in session, DOM-attributes, a form or keep it in JS?
    • How to handle step-back and -forward? Will it reset or safe the results?
    • When, how and where to invalidate the state in case some external parameter is changed?

JS-Frameworks like Vue have very clean ways to structure use-cases like this, for example the vue-leaflet plugin. https://codesandbox.io/s/dd0kit?file=/App.vue:716-1653

But once introducing a JS-Framework other Problems arise:

  • Where is my state now? JS or DOM?
  • Turbo dynamically injects turbo-frames or -streams. How to know when to initialize and UNinitialize these components?
  • Once I’m in Vue- or React-Land, how do I find my way back into the Hotwire ecosystem? (i.e. handing page-flow back to turbo)

I tried solving the before mentioned problems with StimulusJS as it provides lifecycle callbacks, has the Value-API for state-management and integrates nicely with Turbo. I found a few “mounting xxx-JS components with StimulusJS” blog posts: https://www.davedkg.com/stimulusjs-and-mounting-a-react-app or https://gist.github.com/ryenski/c9de022dc94c16131971b64d49c0778d

I basically copy pasted the identical 20 LOC snippets to a hand full of stimulus-controllers the achieve my goal, but I came to the conclusion that “this code is too WET, I need to get it DRY”.

Prototype

To not have to repeat the “mounting”-logic every time I tried to come up with a nice and unified way to mount components to DOM and have their State managed in an “Adapter”-Style. To come up with a more generic approach, I tried to write such an adapter for Vue, React and Svelte - trying to keep the interface consistent.

All adapters implemented have 3 component-lifecycle stages, 3 component functions and 2 framework-references.

Lifecycle Stages:

  • create
  • mount
  • unmount

Functions:

  • change:
    • the component communicates state-changes bottom-up
    • the stimulus-controller communicates state-changes top-down
  • action:
    • the component can call stimulus-controller actions
  • setProperty:
    • the stimulus-controller sets some property on the component that’s not managed by the Value-API

Framework references: The adapters should NOT include any React, Vue or Svelte specific code. So the developer has to pass references to integral framework-parts.

  • setRenderFunction:
    • Vue3 and react need to know how to put HTML in the DOM
  • setFactory:
    • Vue2 and React need to know how to create a new Component instance

I published a working demo that shows the results. https://stimulus-component-api.konow.ski/ Please also have a look at the git-repo. It needs some more refactoring, but it’s in a presentable state.

Required functionality

StimulusJS would need a new API that allows to

  1. register component-handlers and
  2. register framework-components

Example:

app.js

import { ComponentRegistry } from "@hotwired/stimulus"
import { ReactComponent } from "stimulus-react-adapter"

import { createRoot } from 'react-dom/client'
import { createElement } from 'react'

ReactComponent.setFactory(createRoot)
ReactComponent.setRenderFunction(createElement)

ComponentRegistry.register('react', ReactComponent)

my_controller.js

import HelloComponent from "../react/HelloComponent"

export default class extends Controller {
  static components = [{
    type: 'react', target: 'mountpoint', component: HelloComponent
  }]
  static values = { text: String, counter: Number }
}

And then stimulus automagically mounts HelloComponents to every mountpoint-target entering the DOM, using the ReactComponent-Adapter and injects the values as react-props.

The Question

Can you (the maintainers) imagine such a functionality to be part of Hotwire?

I see three ways to implement such a feature:

  1. entirely outside the hotwire ecosystem: Just like the popular https://stimulus-use.github.io/stimulus-use/#/ extensions.
  • Create useReact, useVue and useSvelte and anybody can import it.
  1. partially inside hotwire ecosystem: component api inside, adapters outside.
  • the api (something like I proposed) is implemented in stimulus core and you can then yarn add stimulus-react-adapter
  1. All in: Maintaining a component-API and a hand full of popular adapters inside the hotwire ecosystem.

I’d be happy to go for (and contribute to) any approach, but I’d like your Feedback before I produce a (possibly rejected) PR on the stimulus Repository.

Issue Analytics

  • State:closed
  • Created a year ago
  • Reactions:6
  • Comments:9 (1 by maintainers)

github_iconTop GitHub Comments

1reaction
KonoMaxicommented, Nov 18, 2022

Maybe “island architecture” goes into the right direction, but it feels a bit too heavy right now as an approach - maybe after maturing a bit further I’ll give it a try 😃

Regarding Turbo integration - I’d not drive it that far. When a turbostream yanks in a <div data-controller="test"><div data-test-target="mountpoint"></div></div>, I just want it to use the mountpointTarget as a component root. When the target/controller is deleted it should be gracefully unmounted. Nothing fancy there. For the rest we’ve got JSON and HTML-Forms 😃

As I got a rails project in maintenance where I mounted a hand full of Vue2 components (just 3 or 4 actually), I finally decided to build the stimulus-component-api-package… Now my code looks a bit cleaner and maybe others will enjoy it, too.

In case someone wants to fiddle around with it: Check out the NPM-Package or dive into the source

1reaction
misterhtmlcsscommented, Sep 25, 2022

You should just proceed at No.1 now that you see no one has responded. This is a great idea. I think it’s too bad no one responded because allowing for some kind of plugin with Stimulus would just allow us devs to make out house bigger and include more people inside the house which is good for rails et al in general. Nice work!

If you decide to chase down option No. 1 please tag me and I’ll come check it out. Happy to try contribute.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Vue Composition API Tutorial: Build a Project Idea Generator
Learn how to use the Vue 3 Composition API to build a project idea generator.
Read more >
Crafting Component API, Together - Medium
A design system aspires to achieve a shared vocabulary between designers and developers. As we build visual style and UI components, ...
Read more >
Introducing The Component-Based API - Smashing Magazine
A component-based API is able to make a single request to the server by requesting the data for all of the resources in...
Read more >
Ideas API | Aha! software
REST API to create a custom integration with a third-party system. This guide provides an overview of how to update an idea's visibility....
Read more >
Fusion 360 Help | Documents, Products, Components ...
The structure and access to Fusion 360 data is done through the Document, Product, Component, Occurrence, and proxy objects. Successful use of the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found