tweakpane / use-tweaks

🎛️ Tweak React components with Tweakpane
https://codesandbox.io/s/use-tweaks-example-58e02
MIT License
766 stars 22 forks source link

Handling multiple instances of a component with tweaks #36

Open Pocket-titan opened 3 years ago

Pocket-titan commented 3 years ago

Hey! Love the library :)

I have multiple instances of a component with a useTweaks call, say:

const Button = ({ text }: { text: string }) => {
  const { color } = useTweaks("Buttons", { color: "blue" });

  return <button style={{ backgroundColor: color }}> {text} </button>
}

However, every single instance Button will add its own color setting in the tweakpane, whereas my intent was to have them all share the same value. My question: should I just hoist the useTweaks call up one level and pass the tweaked value down as a prop (feels kind of meh)? Or is there a nicer solution to this? I tried passing the same presetKey but they still have different tweakpane inputs. Here's a sandbox demonstrating the "issue": https://codesandbox.io/s/gracious-bas-22vg2?file=/src/App.tsx.

gsimone commented 3 years ago

While yes, to solve this you should hoist up the tweaks and then pass down the props or use context, it might be a common enough case that we would want to solve. Cc @dbismut what do you think?

dbismut commented 3 years ago

If we do this then we should use zustand as you initially thought @gsimone 😂 That's essentially the same as:

const { color } = useStore(s => ({ color: s.color }))

I think it makes a lot of sense.

EDIT: I think we would need to prevent unnecessary renders of components that don't subscribe to the whole state. Not sure zustand is the right one here, maybe jotai but that would mean having a provider...

dbismut commented 3 years ago

@Pocket-titan It's harder than I thought. It needs to keep track of all panes and nested panes added by components and remove them only when none is being used. I gave it a shot today on the zustand branch here https://github.com/pmndrs/use-tweaks/tree/zustand.

I'm not proud of this: it is bug prone and really feels like we're fighting Tweakpane and React at the same time. that's terrible

Pocket-titan commented 3 years ago

I see! Now that you mention it, another difficulty comes to mind:

// Have to add this "unique" tweak to its own folder to make it work useTweaks(Suzanne number ${id}, { ...makeButton("Remove", () => { remove(); }), })


where `id` is some kind of unique instance identifier. Feels like this is more of a niche edge-case though.

At first I thought that maybe `WeakMap` could help but you're probably right that fighting both React and Tweakpane will cause more issues to pop up along the way.

Here's an idea: what if we check for the `presetKey` option in the schema, and use *only* that (no nested folder/key structure) to determine equality? Meaning that if I want to have a tweak that's shared across instances I can pass `presetKey: "shared"` & it won't create multiple tweaks apart from the first one.

(Tbf: this would still require the use of zustand, but maybe it would be easier b/c this way you don't have to deal with the nesting? but using `presetKey` like this is maybe confusing, since in Tweakpane you use it to avoid name collisions, and this way you would sort of *force* the name collision to occur. difficult!)
dbismut commented 3 years ago

Hi @Pocket-titan we got pumped and decided to rewrite something from scratch, that fits React and supports your idea out of the box with no major hassle.

useTweaks({ color: 1, number: 4 }, folder("sub", { a: 2, b: 3 }, folder("sub2", { c: 4 })))

You'll also be able to do:

useTweaks("sub.sub2", { c: 4 })

And get the value. Of course the value will only be initialized once (by the first hooks that gets called).

We'll try to release something in alpha as soon as it's usable.

PS: the idea is to store the data structure but flatten. So that the store looks like:

{
  "valueKey": value
  "folder.subfolder.valueKey": value
  // ...
}
gsimone commented 3 years ago

If anybody comes across this and wants to try alphas of the new version, drop me a DM on twitter or an email!

joeyfigaro commented 1 year ago

@gsimone what ended up happening with this?

cocopon commented 1 year ago

@joeyfigaro They are currently working on another library leva. This repository was transferred from them to the official Tweakpane organization but there is no active maintainer at this time. I want to handle it but I'm busy updating the core...