Inwerpsel / use-theme-editor

A React theme editor
GNU General Public License v3.0
4 stars 0 forks source link

Signals for React #19

Closed Inwerpsel closed 1 year ago

Inwerpsel commented 1 year ago

Similar to how the get object is created to provide easy hook accessors, it could also fabricate signals for each piece of app state.

Easy accessors as example

Signal implementation would have very similar logic to the existing easy accessors.

https://github.com/Inwerpsel/use-theme-editor/blob/c92d1a268116bf8df12aea79b832cfc03796d21f/src/state/index.ts#L52

https://github.com/Inwerpsel/use-theme-editor/blob/c92d1a268116bf8df12aea79b832cfc03796d21f/src/functions/getters.ts#L14

https://github.com/Inwerpsel/use-theme-editor/blob/c92d1a268116bf8df12aea79b832cfc03796d21f/src/components/inspector/VariableControl.jsx#L168

Normal signals

These signals are just a plain React component that does nothing more than call the hook and immediately returns the value.

   <span>I'm a string with a signal for <$.width /> in the middle.</span>

Need to find a clear cut use case that would benefit from this. It's a bit hard to find because most of this repo's components tend to do multiple things with a single piece of state.

Derived signals

It can also support "derived signals" making use of the currently unused useGlobalMemo hook.

function calculateArea(get) {
  return get.width * get.height;
}

export const use = {
  // ...
  area: 
    () => [useGlobalMemo(calculateArea)]
}

export const $ = __implement_me__(use);
   <span>I'm a string with a signal for <$.area /> in the middle.</span>

The latter works really well but unfortunately is limited to the case where there is a single value for the function and no variants coexisting.

I have yet to find a place in this repo where it could be used. This is probably due to the fact that most things make some kind of use of the inspection result, and this isn't stored in hooks. It's just injected into the main component. And it changes all the time.

But maybe with some changes it can be introduced. Splitting up memos is maybe also an option.

I'm pretty sure that for more regular application state management this technique can be used much more frequently. As long as all inputs live inside an app state hook, and only 1 value is needed at a given time, it would work.

Open questions

Cost of "magic object" approach

Does the magic object step add any significant overhead? Could it be compiled down to something more simple?

I think it's probably fine and may even help reduce bundle size. From a certain point (say 20 different such hooks) shipping individual functions 3 (or more) times results in a significantly larger payload, compared to just shipping the hook and deriving on the fly.

In this repo, the performance cost of going through the object is nowhere near the order of magnitude of the current performance bottlenecks. And because it allows techniques that avoid excessive rendering of components, it usually has a way higher benefit.

Inline at compile time?

In theory, it should be possible to inline all such functions. This would get rid of the additional key lookups that had to be introduced to make these hooks work. Since the exact same code would be inlined in each place, the compressed size should be about the same as having the same name in all such places. For payload size it probably won't matter a lot. But the runtime benefit of skipping the lookup step could be significant, especially if many signals would be used.

Inwerpsel commented 1 year ago

https://github.com/Inwerpsel/use-theme-editor/commit/c2e3fdf6e6e3abc44abbcd2b0d38d20c85d36b0c