-
Notifications
You must be signed in to change notification settings - Fork 187
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support state #66
Comments
As an alternative to consider last week we did some thinking on if we would be able to let people set hooks within the JSX somehow using say a custom component. Not sure if this should be something that is directly implemented into Playroom itself but thought the idea was interesting. I do wonder though if and how limiting this would be. <PlayroomState defaultState={false}>
{(toggled, setToggled) => {
<Button onClick={() => setToggled(true)}>Open</Button>
<Modal
open={toggled}
onClose={() => setToggled(false)}
>
Modal content
</Modal>
}}
</PlayroomState> Or to make it even easier we have also thought about creating helpers that have default state values to allow non-devs to understand and rapidly prototype using state even quicker: <ToggleState>
{(isToggled, toggle) => {
<Button onClick={toggle}>Open</Button>
<Modal
open={isToggled}
onClose={toggle}
>
Modal content
</Modal>
}}
</ToggleState> As we plan on trying to use Playroom as a way for our designers to prototype setting state using the least amount of JS complexity would be the ideal scenario for us. This doesn't have to be something that is necessarily directly integrated into Playroom though. I can for example imagine that within Playroom we just want some way of setting state and then devs can create their own wrappers around that depending on their target audience. |
Here's a quick-n-dirty pull request that creates some wrapped functions and uses context magic to enable no-config state that gets us pretty close to what I consider to be the ideal prototyping API from a designer's perspective. I see an approach like this as following the 80/20 rule. Any components that require state, or any components that require lots of config, would follow this approach. That gets you to about 80% of your rapid prototyping needs. Having hooks available gets you the other 20% of the way there. I see this as the ideal compromise for designer prototyping productivity. The API this gives us for building a stateful Popover with Polaris then is really simple in Playroom: <Play.ToggleState>
<Play.Popover
sectioned
activator={<Play.ToggleButton>Toggle</Play.ToggleButton>}
>
<p>Some popover content</p>
</Play.Popover>
</Play.ToggleState> |
That's interesting. Anything component-based is inherently supported by Playroom already. I see the potential here for people to create component-oriented state management libraries designed for prototyping. If you come up with something along these lines, I might see if I can find a way to talk about it in the readme. |
You can already kind of do this in your components file: import { Checkbox as CheckboxComponent } from './Choice';
export const Checkbox = props => {
const [checked, set] = React.useState(props.checked);
return (
<CheckboxComponent
{...props}
checked={checked}
onChange={() => set(!checked)}
/>
);
}; Having a way to do this without creating the wrapper though would be interesting |
I just got a very simple solution: In my components file, I export this one line component: export const FunctionWrapper = ({ children }) => children() so it allows me to do the following on playroom: <FunctionWrapper>
{() => {
const [state, setState] = React.useState(0);
return (
<>
<p>{state}</p>
<button onClick={() => setState(prevState => prevState + 1)}>
Increase
</button>
</>
);
}}
</FunctionWrapper> |
Hi. Since code now transpiled through babel, it's possible to alter it, so it would be component itself. I coded some solution, that takes user code, wraps it into const [value, setValue] = React.useState('');
<input value={value} onChange={event => setValue(event.target.value)}/> is transformed to React.createElement(function() {
const [value, setValue] = React.useState('');
return <input value={value} onChange={event => setValue(event.target.value)}/>
}) I see interesting side-effect of this solution - you don't need Right now solution is fastcoded as PoC, so there is few broken tests. If you interested, I can continue working on it. |
This would be a very neat improvement! Any way I can help on this? 😄 |
I just went with an IIFE: {(() => {
const [possible, setPossible] = React.useState(false);
return (
<p onClick={() => setPossible(true)}>
Can I use state in playroom: {possible ? "yep" : "dunno"}
</p>
);
})()} |
@pascalduez Your approach is definitely simpler, but only recently became possible because the internal component that wraps your JSX used to be a class component, so Hooks were not supported. (Just in case anyone was wondering why this hadn't been tried earlier) |
That's it! I remember an unsuccessful try some time ago, so I was happily surprised yesterday :) |
Another workaround: use React's
export const State = (props) => {
const result = React.useState(props.default)
return props.children(result)
} And use it like so: <State>
{([state, setState]) => {
return (
<button onClick={() => setState(!state)}>
{state ? "Click me" : "Hey!"}
</button>
);
}}
</State> |
I've been looking into how we can have Playroom support state in a nicer way, and have rediscovered this issue. Mark's initial example is interesting, and I started looking at ways we could use Babel to compile this. I discovered @ezhikov's attempt which seemed like a good start: ezhikov@9af06f0 But I quickly realised that the Babel JSX parser has a serious limitation: It throws an error when there are adjacent JSX elements not wrapped in a fragment. After a few false starts I realised: Mark's example is just MDX! Try pasting the below example into the MDX playground, then select the
The The AST also comes with positional information. So, using this, we can parse the code with A touch complicated, and it assumes What do you think @markdalgleish @michaeltaranto? I'm happy to spend a bit of time working on a PR to give this a crack if you think it's worth it? |
Hey Jess 👋 , this was a bit of a walk back through time reading all the comments, moving from class to function components etc. 😄 We took a different approach in Braid and ultimately ended up building the custom scope feature for Playroom to support it. The scope feature allows variables to be provided into the frame context, which can be used for variables or theme tokens, but equally functions for state interactions. We have found this to be pretty flexible to our needs, and remember considering moving it to Playroom: // playroom.config.js
const stateScope = require('playroom/scopeTemplates/state');
module.exports = {
scope: stateScope,
...
} Here is a working example in the Braid Playroom. What are your thoughts on this approach? |
Loving the custom scope feature! We're currently using it to expose all of React's exports so we don't have to write The custom state functions are interesting, but they still require some funky syntax to get working, and don't appear to support arbitrary JS like @pascalduez's IIFE solution does. For context, we're auto-generating Playroom links by loading example code from our documentation site. That example code might be POR (Plain Ol' React 😝) which uses state, or has JS logic baked in before rendering. Right now, we're automatically inserting an IIFE which makes everything work, but adds ugly syntax to the code editor, and is hard to remember/get right when writing an example from scratch. Ideally, your example could be rewritten like:
|
On reflection with @gwyneplaine, we realised that what we really want is just a way to hook into the Babel processing step; We can perform any IIFE wrapping or MDX parsing there without having to bake our strong opinions into Playroom. To that end, I've opened a PR adding a new
What do you think of this approach? If we end up building something more complex, I'm happy to post it here for future folks to discover and add to their own projects. |
It'd be great if users could make their designs stateful.
My thinking is that we could do this in a code-oriented way by allowing usage of React hooks like this:
For this to work, we'd need to treat the last n number of JSX elements as an implicitly returned fragment, i.e. behind the scenes we'd turn it into a component that looks like this:
The text was updated successfully, but these errors were encountered: