Reputation: 1373
Say I got a elements tree like this:
<Form>
<FormItem>
<Input></Input>
</FormItem>
<Input></Input>
<MyInput></MyInput>
</Form>
I want to disable all the Input
and MyInput
components.
Instead of adding {disabled: true}
to all these Input components, I want to traverse and add disabled prop automaticly. So when I remove or add Input
Component I should not concern the disabled
prop.
Upvotes: 1
Views: 1298
Reputation: 5753
This goes outside the "react" way of doing things, but you can still pass custom events within your application.
If you attach event handlers inside your leaf components, you can listen for these events, emitted from other parts of the application.
I'm using hooks in these examples, but the same can be accomplished in class-based components using componentDidMount
and componentWillUnmount
SomeLeaf.jsx
const SomeLeaf = () => {
const [disabled, setDisabled] = useState(false)
useEffect(() => {
const handler = () => {
setDisabled(true)
}
document.addEventListener('disable-all-the-things', handler)
return () => document.removeEventListener('disable-all-the-things', handler)
}, [])
return (
<input disabled={disabled} />
)
}
SomeOtherComponent.jsx
const SomeOtherComponent = () => {
const handleClick = () => {
const event = document.createEvent('CustomEvent')
event.initCustomEvent('disable-all-the-things', true, true, {})
document.dispatchEvent(event)
}
return (
<button onClick={handleClick}>Disable ALL the things</button>
)
}
Upvotes: 0
Reputation: 15247
Neither of the provided answers are what OP is looking for, which is clear here:
I want to traverse and add disabled prop automatically
To traverse the children of a React component, check out this.props.children
and the React.Children
helpers. You might be able to make a container component that iterates its children recursively, modifying the leaf nodes (those without children) to add the disabled
prop.
For an example, check out https://mxstbr.blog/2017/02/react-children-deepdive/#manipulating-children.
Upvotes: 2
Reputation: 6239
Passing props down is the React way, however if you absolutely insist that you don't want to do that, there are a couple of options that spring to mind:
Use some kind of state container e.g. Redux
You can centrally manage the isInputDisabled
flag in Redux state via Actions and Reducers. Then you wire each of your Input
and MyInput
components into your Redux state via react-redux. When the Redux state changes, all components wired into it via react-redux will be notified and render accordingly.
These two video tutorials from Dan Abramov (the co-creator of Redux) are a great introduction and advanced guide:
Use React Context
This is considered a more advanced feature and is discouraged by the React team where avoidable. It does become a bit "magic" and I personally don't like it as you no longer have the transparency of what props a component takes. From the context docs:
The vast majority of applications do not need to use context.
If you want your application to be stable, don't use context. It is an experimental API and it is likely to break in future releases of React.
If you aren't familiar with state management libraries like Redux or MobX, don't use context. For many practical applications, these libraries and their React bindings are a good choice for managing state that is relevant to many components. It is far more likely that Redux is the right solution to your problem than that context is the right solution.
If you aren't an experienced React developer, don't use context. There is usually a better way to implement functionality just using props and state.
If you insist on using context despite these warnings, try to isolate your use of context to a small area and avoid using the context API directly when possible so that it's easier to upgrade when the API changes.
Upvotes: 1
Reputation: 3940
First of all you need to define a state in the parent component, corresponding to the disabled
attribute of your input components, let's call it this.state.isInputDisabled
. So having that, you can pass it as a prop
:
<Form>
<FormItem>
<Input isDisabled={this.state.isInputDisabled}></Input>
</FormItem>
<Input isDisabled={this.state.isInputDisabled}></Input>
<MyInput isDisabled={this.state.isInputDisabled}></MyInput>
</Form>
And then when you change the state in the parent component, you also need to update children. To to that override componentWillReceiveProps
method, for example:
class MyInput extends React.Component {
componentWillReceiveProps(nextProps) {
this.forceUpdate();
}
render() {
return <input type="text" disabled={this.props.isDisabled} />
}
}
To answer your comment:
What piece of code do you actually expect to handle the disabled
attribute for you? There are two other ways to manage the children state from the parent I can think of:
Store the references to input components in refs
and manage them in the parent (but again this involves an additional prop
you want to avoid).
Grant a specific HTML class for rendered components and just use vanilla JS to update the disabled
prop:
function toggleInputDisabled(disable) {
document
.getElementsByClassName('your-input-class')
.forEach(elem => elem.disabled = disable);
}
Using vanilla JS is not forbidden in React, however it is considered a bad practice and you should go for it only if you see no simple way out.
Upvotes: 1