BuilderIO / mitosis

Write components once, run everywhere. Compiles to React, Vue, Qwik, Solid, Angular, Svelte, and more.
https://mitosis.builder.io
MIT License
12.29k stars 542 forks source link

Compile away hooks #548

Open steve8708 opened 2 years ago

steve8708 commented 2 years ago

Purely an idea, but something been thinking about a lot. One of the biggest missing DX features of Mitosis is custom hooks

Interestingly, some ppl have been exploring making hooks libraries across frameworks

If you think of someone building an app in Mitosis vs a framework like Qwik directly, you miss out on one of the main composability benefits, custom hooks. Such as a react-query like hook (called Resource in Qwik and Solids) etc. I can't see people loving building with Qwik without this key feature nearly all modern frameworks have (react, vue, svelte, qwik, solid) that Mitosis doesn't

I realized I think we can implement this for all hooks-oriented frameworks. We may even be able to implement it for frameworks without first class hooks support.

We could do it, for instance, with another convention just like we have .lite.tsx for components, and .context.tsx for context, we could make a .hook.tsx

// ./use-fetch.hook.tsx
import { useState, onMount } from '@builder.io/mitosis'

export default function useFetch(url: string) {
  const [value, setValue] = useState(null)
  const [error, setError] = useState(null)
  const [loading, setLoading] = useState(false)

  onMount(async () => {
     setLoading(true);
     try {
       const data = await fetch(url).then(res => res.json())
     }  catch (error) {
        setError(error)
     } finally { 
       setLoading(false)
     }
     setValue(data)
  })

  return { value, loading, error };
}

which would compile to other frameworks just like you see in mitosis components name

I think translating hooks like this is pretty straightforward to everywhere that supports hooks. the big question is what about frameworks, like Angular, that don't. I have some crazy ideas for that but need to think on a little more

steve8708 commented 2 years ago

this as a feature could be useful standalone to those like tanstack too, to make hooks that support all hooks-oriented frameworks that you write once and it compiles out to all. and of course benefits mitosis components and libraries too

samijaber commented 2 years ago

Fully agree, providing some form of "hooks" support is probably a critical next feature for Mitosis. It's been on my mind...folks need a way to encapsulate complex logic and reuse it across components. https://zagjs.com/ Does somewhat of the same thing as Tanstack (hooks lib tailored for finite state machines, with framework-specific bindings for React/Vue/Solid)

I think translating hooks like this is pretty straightforward to everywhere that supports hooks. the big question is what about frameworks, like Angular, that don't. I have some crazy ideas for that but need to think on a little more

Yeah, I'd want to lay out a rough plan outlining what this would look like in the most "important"/"relevant" 5-6 frameworks to ensure it would work in enough places (Vue/React/Svelte/Solid/Qwik). I see it working with none/very little work for react/svelte/solid, but also need to think a tiny bit about Vue. They have 2 different API styles now, there could be some quirks:

Composition API is also Vue3 only I believe, so that's another thing to keep in mind.

Also concerned about how much additional scaffolding would need to go into making hooks work in webcomponents/html, which could lead to us potentially implementing a full web framework's features to get them to work.

steve8708 commented 2 years ago

Agree w/ all of that. And also currently don't think this is any level of priority but I have some vague ideas on how to support webcomponents/html/angularetc. Perhaps a little library could/would be needed. I could see that as an interesting project in general - framework agnostic simple hooks system that can plug into any framework that doesn't currently support it. This is a bit like how mobx works (and solidjs reactivity, etc). Not sure what tanstack and others do, I am assuming they just manually implement across but maybe they have a helper system we can draw inspiration from

But perhaps a little helper library one day is the ultimate solution. Support hooks for frameworks that support hooks (via direct compilation, no lib needed), and at some point someone can create a framework agnostic hooks primitive library that this could output to as well

Interesting to think of how a small library could bring hooks to angular and other frameworks. Could be interesting to those communities

leopiccionia commented 2 years ago

Composition API is also Vue3 only I believe, so that's another thing to keep in mind.

FYI Vue 2.x supports the Composition API natively since 2.7 (released weeks ago). For earlier releases, you need the @vue/composition-api plugin.

Vue 2.7 Composition API works like Vue 3 Composition API, except when otherwise noted.

samijaber commented 2 years ago

@leopiccionia Thanks a lot for sharing, that's very valuable information!

digoburigo commented 2 years ago

Imagine VueUse working in all frameworks. That would be really nice.

jdgamble555 commented 1 year ago

I think it is very important to talk about what we are translating here. A lot of internal and external functions in frameworks have similar features that overlap, so let's get as precise as possible.

I am trying to find the simplest, most direct equivalents; some items on here can do all of these.


Passing data from parent to child

These generally are just passing the data, no binding.


Passing data from parent to X grandchild

Basically the same thing for grandchildren:


Passing data from X grandchild to grandparent

What are we really talking about here?

  1. Shared State - Providing a shared function or class at the top-level - whether it be a provider in a module--component or a context function that can be called from a child to keep state. Child components may need to evoke an emitter to push the DOM changes if necessary
  2. Caching computation - some functions like React's useCallback or useMemo can help with speed
  3. Subscribing to changes - useState (React), rxjs observables (all Javascript but mainly Angular), or stores (Svelte)

There is really a weird combination of all three of these things in our frameworks, but they are by far the easiest to work with using Svelte Stores, Angular Services, and RXJS Observables.


Other things to consider for this project:

It would be cool to build a basic app to see how similar and exact you could get these features --- a todo app for example.

J