-
In DySys office hours @aayushpi asked me to take a look at our components that use Reakit, specifically the I do have one concern about the Are there any alternatives or plugins for |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 6 replies
-
Disclosure is a relatively simple component and something we could refactor whilst keeping the same API and palm off handling the "visible" state of the component to the consumer. There's not a whole lot of danger in regressing the accessibility of the control. What I am curious in though is posing the following question to Paste consumers: Given that components must follow the W3C ARIA authoring practices, the majority of which come with pretty complex keyboard and user interactions that require multiple internal parts of the component to stay in sync to achieve an accessible user interaction, what would be your proposal as an alternative? |
Beta Was this translation helpful? Give feedback.
-
I'd like to remark that while uncontrolled components are a poor developer experience on DOM (namely form) elements, it is actually a positive developer experience for complex stateful UI component interactions. You may already know much of what I’m about to say below, but for the sake of clearly documenting my rationale to other possible readers, I'm going to break down how I came to the above conclusion: Differences between controlled and uncontrolled componentsControlled components require two things to work: the current state to render, and [1..n] callback functions for you to manage that state. When the controlled component needs to change, it fires the corresponding callback function with some (optional) parameters. Basic controlled input example: const ControlledInput = ({value, handleChange}) => {
return <input value={value} onChange={e => handleChange(e.target.value)} />
} Your code that wraps controlled components must define and provide this callback handler logic. That logic typically updates the state you maintain, and that state in turn gets passed down to the controlled component for the update to become visual for the user. Basic wrapping code: const App = () => {
const [username, setUsername] = React.useState(‘’);
return (
<ControlledInput value={username} handleChange={setUsername} />
);
} In summary, controlled components hold no state. It is up to the developer to manage the state and pass new props when appropriate to re-render these components. This gives you the power to manage that state the way you want, at the cost of having to do some extra coding work. Uncontrolled components require one thing to work: the initial state. These components manage state internally, providing all the pros/cons of code encapsulation in general. Passing a new state does not force an update to the internal state, it is simply ignored. Uncontrolled components usually provide some mechanism for you to track the current state value. If your uncontrolled component wraps a DOM element, the only way to retrieve the current value (its state), is to use Basic uncontrolled input example: const UncontrolledInput = ({initialValue}) => {
return <input defaultValue={initialValue} />
} Your code that wraps uncontrolled components can rely on the fact that the component will just work. You cannot change how these components manage state, you get what you get. For a simple example, you can’t make it so that every time the user types the letter “a” it shows up as “x”. Basic wrapping code: const App = () => {
return (
<UncontrolledInput initialValue=”” />
);
} In summary, uncontrolled components hold all the runtime state. It is up to the developer to seed in the initial state and, if necessary, keep tabs on the last state for storage purposes. This provides convenience at the cost of control. Official docs for more reading:
Pros and ConsControlled Component Pros:
Controlled Component Cons:
Uncontrolled Component Pros:
Uncontrolled Component Cons:
Why do we recommend using controlled components for forms?
Conventional wisdom recommends controlled components for forms because forms are made up of disparate elements that share common state. For example, changing a selection of one dropdown might affect the visibility of another element. It makes sense to manage that state yourself to control the presentation of the UI. Another benefit is easily serializing the data to send to a server or to localstorage. The concern of data for forms isn't really attached to any one element, it's attached to the whole form. Why I think uncontrolled components are sometimes fineIn the case of Disclosure and Menu I think uncontrolled components are a more convenient paradigm to achieve a consistent UX. As a developer, you really only care to set the initial state of the component and the user should be able to toggle things at will. There usually isn't an interdependence between Disclosure & Menu and some other part of your app; it's an isolated interaction. The state is usually ephemeral, being discarded when you navigate off the page or click away. Accessible Menus are complex to build because they have many possible events they need to manage (i.e.: keyboard nav), so wiring all the possible event handlers could be tedious and result in different behaviors for consuming products. Managing the current state of an input’s current value is pretty trivial, while managing the state of more complex interactions like in a Menu is fairly demanding. For most applications of Menu, setting some initial state is enough as their active state shouldn't be pertinent to the rest of the application. In some cases you may want to keep a copy of the state (i.e.: is the disclosure open?) for the next time the user lands on the page, and there usually is a simple way to do that because these are React components and not DOM elements. From a design system point of view, we’re aiming to maximize leveragability by solving for the common denominator (pareto principle). In our case, it makes sense to abstract these complexities in an uncontrolled component. As a result consumers can get these components working correctly and easily for most applications. Whew, big wall of text, but I hope this helped clarify why I think this is a reasonable decision for us to make. If you encounter a situation where the encapsulation of state is blocking a certain UX/UI design, please bring that to crit and we can evaluate a path forward based on a more concrete example. |
Beta Was this translation helpful? Give feedback.
-
For transparency into an offline discussion I had with @hharnisc, we'll dig into the possibility of following this pattern and providing a stateHook prop to components so you can use composition with Reakit state and your own state to give greater control over the state the component is using. Obviously with some warnings that you can totally destroy the accessibility of the component if you're not caareful. |
Beta Was this translation helpful? Give feedback.
-
@hharnisc I've opened this PR adding a stateHook (name TBD) prop to Disclosure. Will you take a look and let me know if this will meet your needs? TIA. |
Beta Was this translation helpful? Give feedback.
For transparency into an offline discussion I had with @hharnisc, we'll dig into the possibility of following this pattern and providing a stateHook prop to components so you can use composition with Reakit state and your own state to give greater control over the state the component is using.
Obviously with some warnings that you can totally destroy the accessibility of the component if you're not caareful.
https://reakit.io/docs/composition/#state-hooks