pursuant to this discussion https://twitter.com/dan_abramov/status/1001943857096003584
all react components can be:
the current docs on controlled vs uncontrolled: https://reactjs.org/docs/uncontrolled-components.html focus entirely on forms, but this design pattern applies to -all- components. we should document this.
questions
The concept of control in components extends beyond forms. Here, we try to document the 2 common design pattern choices you will want to think about in creating your React Components.
First, definitions. "Uncontrolled" components maintain their own state.
For example:
class UncontrolledToggle extends React.Component {
state = { isOn: false }
handleChange = () => this.setState(state => {isOn: !state.isOn})
render() {
const { handleChange, state } = this
return (
<div>
State: {state.isOn}
<button onClick={handleChange}>Toggle State</button>
</div>
)
}
}
Usage:
<UncontrolledToggle />
In contrast, "controlled" components do NOT maintain their own state. Instead, their state comes through as a prop, usually a "value" prop + an "onChange" prop.
For example:
function ControlledToggle({ handleChange, isOn }) {
return (
<div>
State: {isOn}
<button onClick={handleChange}>Toggle State</button>
</div>
)
}
Usage:
// define isOn and handleChange elsewhere
<ControlledToggle handleChange={handleChange} isOn={isOn}/>
In other words, any component that you create can have 2 possible modes: either it controls itself and maintains its own state, or it is controlled by a parent.
I would very much like to see a "canonical" approach for the mixed mode in the docs. This is the most cumbersome to re-implement in React 16.4.
An example of component needing mixed control — i.e. a state-from-props approach — from my own codebase is a text input in which the value is committed only when pressing the Enter key.
The blog post on React 16.4 mentions an approach:
static getDerivedStateFromProps(props, state) {
const prevProps = state.prevProps;
// Compare the incoming prop to previous prop
const controlledValue =
prevProps.value !== props.value
? props.value
: state.controlledValue;
return {
// Store the previous props in state
prevProps: props,
controlledValue,
};
}
I wonder if there's another way to think about this sort of scenario.
@sw-yx in regards to your Draft:
componentDidUpdate lifecycle method://...
componentDidUpdate(previous_props, previous_state) {
if (this.state.isOn !== previous_state.isOn && typeof this.props.onChange === 'function') {
this.props.onChange(this.state.isOn);
}
}
//...
It's also apparent that I've been using mixed control to mean something slightly different: taking the value from the prop, forking it in the state, and reacting to changes in the prop — it's now clear that the approach is bug-prone, but in the example from my previous comment, I believe there needs to exist a flow to address these kinds of things...
May I propose we use a slightly more complex example to explain the two patterns? I was thinking something along the lines of re-implementing <input type='range' min='0' max='100' step='10'/> as a custom component. It will surface, I think, more details on how to approach common problems. (Although it may not be the best example, since it needs some CSS to work correctly, and that may be a rabbit-hole too deep to go into). But for controlled components, should we suggest as an optimization to check that the value actually changed before broadcasting it on the onChange callback? Multiple mouse positions during drag will correspond to the same value (due to the step attribute), so the component is responsible for de-duplicating them?
@danburzo these are good questions. i think we can err on the side of having more examples rather than having one that demonstrates all the bells and whistles. so something like:
something like that?
with this much content, and with the importance of the topic, this probably warrants a new page. is there anyone we should ask to sign off on this? since there would be a "Uncontrolled Components" page and a "Controlled vs Uncontrolled Components" page. how does that big picture thing look?
btw feel free to get started on a PR if anyone watching wants, i dont lay claim to this :)
The upcoming blog post also has some useful info in that regard: https://github.com/reactjs/reactjs.org/pull/931
oh wow, this is a huge blogpost (new pr https://github.com/reactjs/reactjs.org/pull/934) and basically covers what we cover. @bvaughn I wonder if you would want to update the docs alongside the blogpost? I can incorporate a lot of what you wrote into the official docs. I'll bug you about it tomorrow since we're meeting :)
Sounds good to me.
I do think we should have some cross-linking and that maybe some of the post should make its way into the docs as well, but so far I've been focusing mostly on the blog post itself and getting it right.
The blog post is out, I should probably spend some time this weekend firming up the docs. Does anyone have to sign off/decide on adding a new page vs renaming the "Uncontrolled Components" page?
Does anyone have to sign off/decide on adding a new page vs renaming the "Uncontrolled Components" page
Someone like Dan or myself should review the PR, yeah 😄Especially if you're proposing any sort of structural changes. Just to make sure we're all on the same page.
well. we could just stick it inside the Uncontrolled Components page. I don't know. I can write a lot, or very little, or not at all. I am very uncertain about the scope of this issue, and its going to vary a lot from person to person.
@sw-yx that sounds really great. I'd very much like to see the officially recommended way of writing that kind of mixed components.
- simple uncontrolled example
- simple controlled example
- simple mixed example
- broadcast controlled example
- deduped broadcast controlled example
I'm wondering, isn't the "broadcast controlled" example unnecessary? Correct me if I'm wrong but I thought if you want to control the state most likely you also want to be notified of the changes. And could you also explain what you meant by "deduped broadcast"?
Btw. can we find a better name? "Mixed components" to me sounds a bit too generic. Maybe something like "controllable components"? "Mixed-control components"?
sure thing. if anyone wants to take over this issue please do, I don't have a strong vision for it. I would also say we can try to limit the scope for now, so maybe just do the first 3 to get the initial message across. we're not writing a book here. this issue is starting to have some scope creep.
Hi!
I wonder what is an appropriate way of getting uncontrolled component's data, eg: email example from blogpost
Most helpful comment
I would very much like to see a "canonical" approach for the mixed mode in the docs. This is the most cumbersome to re-implement in React 16.4.
An example of component needing mixed control — i.e. a state-from-props approach — from my own codebase is a text input in which the value is committed only when pressing the Enter key.
The blog post on React 16.4 mentions an approach:
I wonder if there's another way to think about this sort of scenario.