More recent answer with an example, which uses React.useState
Keeping the state in the parent component is the recommended way. The parent needs to have an access to it as it manages it across two children components. Moving it to the global state, like the one managed by Redux, is not recommended for same same reason why global variable is worse than local in general in software engineering.
When the state is in the parent component, the child can mutate it if the parent gives the child value
and onChange
handler in props (sometimes it is called value link or state link pattern). Here is how you would do it with hooks:
function Parent() {
var [state, setState] = React.useState('initial input value');
return <>
<Child1 value={state} onChange={(v) => setState(v)} />
<Child2 value={state}>
</>
}
function Child1(props) {
return <input
value={props.value}
onChange={e => props.onChange(e.target.value)}
/>
}
function Child2(props) {
return <p>Content of the state {props.value}</p>
}
The whole parent component will re-render on input change in the child, which might be not an issue if the parent component is small / fast to re-render. The re-render performance of the parent component still can be an issue in the general case (for example large forms). This is solved problem in your case (see below).
State link pattern and no parent re-render are easier to implement using the 3rd party library, like Hookstate - supercharged React.useState
to cover variety of use cases, including your's one. (Disclaimer: I am an author of the project).
Here is how it would look like with Hookstate. Child1
will change the input, Child2
will react to it. Parent
will hold the state but will not re-render on state change, only Child1
and Child2
will.
import { useStateLink } from '@hookstate/core';
function Parent() {
var state = useStateLink('initial input value');
return <>
<Child1 state={state} />
<Child2 state={state}>
</>
}
function Child1(props) {
// to avoid parent re-render use local state,
// could use `props.state` instead of `state` below instead
var state = useStateLink(props.state)
return <input
value={state.get()}
onChange={e => state.set(e.target.value)}
/>
}
function Child2(props) {
// to avoid parent re-render use local state,
// could use `props.state` instead of `state` below instead
var state = useStateLink(props.state)
return <p>Content of the state {state.get()}</p>
}
PS: there are many more examples here covering similar and more complicated scenarios, including deeply nested data, state validation, global state with setState
hook, etc. There is also complete sample application online, which uses the Hookstate and the technique explained above.