Is there a way to access a React component's sub-components? - reactjs

So I know that you can access a component's children with this.props.children:
<MyComponent>
<span>Bob</span>
<span>Sally</span>
</MyComponent>
Which is great if I'm interested in Bob and Sally, but what if I want to interact with the components that make up MyComponent (i.e. Subcomp1 and Subcomp2 shown below)?
render: function() {
return (
<div className="my-comp">
<Subcomp1 />
<Subcomp2 />
</div>
);
},
Use Case
I'm trying to create a higher order component that manages the tab index (roving tab index: https://www.w3.org/TR/wai-aria-practices/#kbd_roving_tabindex) of the wrapped component's sub-components, so it would be great if I could get a ref to the wrapped component and filter it's subcomponents by type.
So far the only approach that seems possible is to have each component store a ref for each of it's subcomponents, but this is tedious and kind of defeats the purpose of an HOC. Is there a generic way to access these sub-components?
A rough example of what I'm trying to do:
var HOC = (ComposedComponent) => {
return React.createClass({
componentDidMount: function() {
const subComponents = this.composedComponent.subComponents; // Something like this would be nice
const menuItems = subComponents.filter(() => {
// figure out a way to identify components of a certain type
});
this.applyRovingTabIndex(menuItems);
},
render: function() {
return (
<ComposedComponent
ref={(c) => { this.composedComponent = c }}
{...this.props} />
);
}
});
};

The tabIndex manipulation need not be done in the HOC, rather it can be done in the Parent component that renders all the HOCs. Because all you need is to determine which sub component is clicked and adjust the selected state on the Parent component. This selected state can then be propagated back to the sub components who compare their index with selected index and assign tabIndex accordingly.
You can send the respective props to determine whether the current ComposedComponent is selected or not by passing an onClick event handler all the way. Then in your sub component you can access tabIndex using this.props.tabIndex and render your parent div as
<div tabIndex={this.props.tabIndex}> </div>
The code below is almost like pseudo code to give an idea. If you feel that this does not solve your requirement you can try out a Tab example worked out by an awesome developer at this link CODEPEN EXAMPLE
const HOC = (ComposedComponent) => {
return class extends React.Component {
render (
<ComposedComponent
tabIndex={this.props.selected === this.props.index ? "0" : "-1"}
{...this.props}
/>
)
}
}
class Parent extends React.Component {
state = {
selected: 0
}
// Set the current selection based on the currentSelection argument
// that is bound to the function as it is sent along to Props
adjustTabIndices = (currentSelection) => (event) => {
this.setState({selection: currentSelection})
}
render {
return (
<div>
{
// These are your various MenuItem components that
// you want to compose using HOC
[MenuItem1, MenuItem2, MenuItem3].map(index => {
const MenuItem = HOC(MenuItem1);
return (
<MenuItem
key={index}
onClick={this.adjustTabIndices(index)}
selection={this.state.selected}
index={index}
/>
)
})
}
</div>
)
}
}

Related

React render specific children

I have looked through other peoples questions relating to this but cant find a suitable answer. I would like to pass children to a component and then pull out the specific children where I want them, most examples I have seen just have the children render in the same place.
My component looks something like this -
<ParentComponent>
<ChildOne/>
<ChildTwo/>
<ParentComponent/>
When I log the props.children inside the parent component I get an array which contains both children as objects. is there a simple way to pull out the specific child where I need it such as {props.children.ChildOne} at the moment I am using props.children[0] which isn't ideal as we will be passing the children dynamically
in the future and the array length may change.
As always any help is greatly appreciated!
Depending on your exact situation and needs, it might make more sense to pass child components as props than using the special children prop. Then you can render them whichever way you like.
<ParentComponent childOne={ChildOne} childTwo={ChildTwo} />
...
const ParentComponent = ({ childOne, childTwo }) => {
return (
<div>
{childOne}
<div>
{childTwo}
</div>
</div>
);
};
But knowing your exact scenario would help a lot with conceptualising the best way to implement this. Perhaps you can refactor your code to avoid passing an array of children like this.
Actually, the ReactChildren API I was mentioning is useless here.
You can do something like this instead:
import React from 'react';
import { ChildOne } from './YourFile';
export function ParentComponent({children}) {
return children.find(child => child.type === ChildOne)
}
You should define the displayName property for the child components and then use the displayName in the parent to find the specific children from children list and place them where you want it to be.
// define displayName for each component, it can be any string
// You can set the displayName for any react component before exporting as shown
// below
const ChildOne = (props) => { return (<div> Child One </div>)}
ChildOne.displayName = "ChildOne";
export default ChildOne;
const ChildTwo = (props) => { return (<div> Child Two </div>)}
ChildTwo.displayName = "ChildTwo";
export default ChildTwo;
Now in parent component you can filter out the specific child by using their displayName.
const ParentComponent = (props) => {
const getChildByDisplayName = (displayName) => {
const child = React.Children.map(props.children, (child => {
// you can access displayName property by child.type.displayName
if (child.type.displayName === displayName) return child;
return null;
}))
return child;
}
return (
<div>
{/* You can change the order here as per your wish*/}
{getChildByDisplayName("ChildOne")}
{getChildByDisplayName("ChildTwo")}
</div>
)
}
That's it, Now even if you put ChildTwo before ChildOne like below example, parent component will still render the ChildOne first and then ChildTwo because we have defined order in parent.
<ParentComponent>
<ChildTwo/>
<ChildOne/>
<ParentComponent/>
Using the key seems simpler:
whatever is using the parent component:
<ParentComponent>
<ChildOne key="title"/>
<ChildTwo key="picture"/>
<ParentComponent/>
parent component:
export default function ParentComponent(props: any) {
const title = props.children.find((o: any) => o.key === 'title')
const picture = props.children.find((o: any) => o.key === 'picture')
return <div>
<jumbobox>
{title}
</jumbobox>
<fancyframe>
{picture}
</fancyframe>
</div>
}
One way is to control the Child Components being passed to ParentComponent through state/props/redux store management in the component containing ParentComponent and its children.
But to have the solution as per your use case, we can avoid using children prop and use our defined prop on ParentComponent.
<ParentComponent
components={[
{key: 1, component: <div>Hello</div>}
]}
/>
So, we can now filter from the key.
Checkout this demo

React.js: how to set an active flag in one of multiple identical children?

I have a parent component with multiple identical children of which only one can be active at a time. The active state is to be set through an internal event on the child itself e.g. a button click, and not by the parent. I want the active state to be unset by a call from a sibling but I cant find a way for siblings to call eachother's methods. Ive tried refs but they are only accessible from the parent and i cant find a way to make a child ref available within itself without maintaining a list of refs on the parent which i dont want as i only need to store the currently active one.
Simple example
e.g.
<Parent>
<Child active={false}/>
<Child active={false}/>
<Child active={true}/>
</Parent>
where a child is something like
export class Child extends React.Component {
constructor() {
super(props);
state = {
active: props.active;
}
}
setActive(active) {
setState ({active : active});
}
onclick = () => {
// get currently active other sibling?
// call setActive direct on other sibling.
// e.g. other.setActive(false);
// set current to active using the same method
this.setActive(true);
}
render() {
return (
<button onclick={this.onclick}/>
<p>current state={this.state.active ? "active": "inactive"}
);
}
}
I've tried passing in parent setActiveRef and getActiveRef functions as props to the children to maintain a single shared ref (the active one) and use getActiveRef().current.setActive directly but i cant find a way to access the ref of a child component from within itself to send to the setActiveRef() on the parent in the first place.
any advice much appreciated. thanks
In short, this isn't how React is designed - children won't be able to modify each other's state directly. A simple analogy would be a literal parent and their children. You want the children to know when it's time to raise their hand, but they don't take directions from each other, only from Mom or Dad. What you CAN do is tell the kids how to communicate with their parents, and let their parents deal with it.
I'm sure there are better ways, but here is some code:
export class Parent extends React.Component {
constructor() {
super(props);
state = {
activeChild: "Jimmy"
}
};
// Let the parents listen for requests
handleChildRequest (name) => {
this.setState({activeChild: name});
};
render() {
<div>
<Child active={this.state.activeChild === "Jimmy"} name="Jimmy" handleRequest={this.handleChildRequest} />
<Child active={this.state.activeChild === "Sally"} name="Sally" handleRequest={this.handleChildRequest} />
<Child active={this.state.activeChild === "Fred"} name="Fred" handleRequest={this.handleChildRequest} />
</div>
};
}
export class Child extends React.Component {
constructor() {
super(props);
// Lets forget about local state, we don't need it!
}
onclick = () => {
this.props.handleRequest(this.props.name);
}
render() {
return (
<button onclick={this.onclick}/>
<p>current state={this.props.active ? "active": "inactive"}
);
}
}
Answer to my own question using component passing (this) references to parent callback. This is not complete code but i illustrates the point. Appears to work fine for my use case (updating realtime map locations) which is more complex than this simplified example.
parent component passes callbacks to children to store ref to active component
export class Parent extends React.Component {
activeChild = undefined;
setActiveChild = (child) => {
activeChild = child;
}
getActiveChild = () => {
return activeChild;
}
// set up some callback props on each child
render() {
return (
<Parent>
<Child active={false} setActiveChild={setActiveChild} getActiveChild={getActiveChild}/>
<Child active={false} setActiveChild={setActiveChild} getActiveChild={getActiveChild}/>
<Child active={true} setActiveChild={setActiveChild} getActiveChild={getActiveChild}/>
</Parent>
)
}
each child simply calls back on the parent using prop callbacks and passes itself. this allows the state to be set internally within the component forcing a re-render if values change.
export class Child extends React.Component {
onclick = () => {
// get currently active other sibling stored in parent
let active = this.props.getActiveChild();
// call setActive directly on other sibling.
active.setActive(false);
// store currently active child in parent
this.props.setActiveChild(this);
// set 'this' component to active using the same method
this.setActive(true);
}}
criticisms and improvements most welcome.
thanks
I was looking for a way to do something similar with hooks, and this is what worked for me:
import React, { useState } from 'react';
const Parent = () => {
const [activeChild, setActiveChild] = useState(undefined);
return (
// Now the children have access to the current active child
// and also the ability to change that
<Child activeChild={activeChild} setActiveChild={setActiveChild} id={'1'}/>
<Child activeChild={activeChild} setActiveChild={setActiveChild} id={'2'}/>
)
export default Parent
then inside the Child component...
import React, { useState, useEffect } from 'react';
const Child = ({activeChild, setActiveChild, id}) => {
const [activated, setActivated] = useState(false);
useEffect(() => {
if (activeChild !== id) {
setActivated(false);
}
}, [activeChild, chapter]);
return (
//on click the component will be able to be activated and set itself as the activated component.
<div onClick={()=> {
setActivated(true);
setActiveChild(id)
}} />
)
export default Child
The useEffect in the Child will check if its id (which is unique to itself) is the id of the activeChild. If not, it'll make sure that its local state of activated is false.
Once activated though, it'll set its local state of activated to true, and set the activeChild's id to its own id.
any feedback is welcome!! This made sense in my head.
Let's supposed you have an array of childs components, each one of those child, will have a prop called active, so you could use an state variable to store the array of childs, so when one of the childs gets updated, and cause a rerender of each one of the child components as well.
import React from "react";
const Parent = () => {
const childs = [{ active: false }, { active: false }, { active: false }];
const [childrens, setChildrens] = React.useState(childs);
const onOptionSelected = idx => {
setChildrens(prevOptions =>
prevOptions.map((opt, id) => {
opt.active = id === idx;
return opt;
})
);
};
return (
<div>
{childrens.map((child, id) => {
return (
<Child
key={id}
id={id}
active={child.active}
onOptionSelected={onOptionSelected}
/>
);
})}
</div>
);
};
const Child = ({ id, active, onOptionSelected }) => {
console.log(id)
const onClick = () => {
onOptionSelected(id);
};
return (
<>
<button onClick={onClick}>set active</button>
<p>current state={active ? "active" : "inactive"}</p>
</>
);
};
export default Parent;

How do I implement an onClick method in one child component that updates the text in a sibling component, based on the state in App.js?

Every row in my SideMenuContainer corresponds to an object from schema.json, showing only the name property. The behavior I want is that when a row is clicked, the PaneViewContainer toggles to display the name and other properties of that respective object from the json.
In App.js, the data is passed to SideMenuContainer like so:
render() {
return (
<MainContainer>
<SideMenuContainer genInfoList={this.state.genInfoList}/>
<PaneViewContainer genInfoList={this.state.genInfoList}/>
</MainContainer>
);
}
In SideMenuContainer, every row is populated like this:
render() {
return (
<SideMenuPane>
<SideMenu>
<div>
<h2>GenInfo</h2>
{this.props.genInfoList.map(genInfoElement => {
return (
<SideMenuRow>
{genInfoElement.name}
</SideMenuRow>
);
})}
</div>
</SideMenu>
</SideMenuPane>
);
}
What I want to do is change the genInfoList information being displayed in the PaneViewContainer based on which row is clicked in its sibling, SideMenuContainer.
The entire genInfoList data is being passed to both sibling components from their parent App.js, so I want to change which portion of that data is loaded in the Pane based on the row clicked in the SideMenu.
I thought about using the Context API, but I'm having trouble figuring out how to implement it for this purpose. Any ideas?
If I understand correctly you have your information stored in the parent element of both components then you can just pass a function down as a prop and have all of your logic stored in the parent element.
changeInfoList = id => {
//change your info list based on id or index or whatever
this.setState({
//your new list
})
}
render() {
return (
<MainContainer>
<SideMenuContainer changeInfoList={this.changeInfoList} genInfoList={this.state.genInfoList}/>
<PaneViewContainer genInfoList={this.state.genInfoList}/>
</MainContainer>
);
}
and then call changeInfoList from your component with props
render() {
return (
<SideMenuPane>
<SideMenu>
<div>
<h2>GenInfo</h2>
{this.props.genInfoList.map(genInfoElement => {
return (
<SideMenuRow>
{genInfoElement.name}
<button onClick={this.props.changeInfoList(genInfoElement.id)>CLick Me</button>
</SideMenuRow>
);
})}
</div>
</SideMenu>
</SideMenuPane>
);
}
this is commonplace in react as you should have smart components and dumb components. When you have components not in the same tree or spread far away then the context api is very useful. In your case I don't think its necessary.
Without external state management, you would have to pass down a callback (as props), so the children can update the parent's state.
As the components get far away from each other, this pattern can get annoying (passing down callbacks each time). That's where external state management can help.
Here's a simple (and untested) example using a callback:
class Counter extends React.Component {
constructor() {
super();
this.increment = this.increment.bind(this);
this.state = {count: 0};
}
increment() {
let count = thist.state.count;
this.setState({count: count + 1});
}
render() {
return <div>
<CounterButton increment={this.increment}/>
<CounterDisplay count={this.state.count}/>
</div>;
}
}
class CounterButton extends React.Component {
render() {
let increment = this.props.increment;
return <button onClick={increment}>Plus One</button>;
}
}
class CounterDisplay extends React.Component {
render() {
let count = this.props.count;
return <span>{count}</span>;
}
}

Call child component method from parent in react

I have simple component called List which is a simple ul with some li inside. Each li is a simple component.
I have other parent component which render one input field and the List component. Tapping on Send key I catch text of input field. I want to call for example a function called handleNewText(inputText) but this function need to stay inside List component because the state I use to populate other li components live in List component.
I don' t want to refactor List and MyParent component passing the manage of data from List to MyParent.
first is parent and second is child
class TodoComp extends React.Component {
constructor(props){
super(props);
this.handleKeyPress = this.handleKeyPress.bind(this);
}
componentDidMpunt(){
console.log(this._child.someMethod());
}
handleKeyPress(event){
if(event.key === 'Enter'){
var t = event.target.value;
}
}
render(){
return (
<div>
<input
className="inputTodo"
type="text"
placeholder="want to be an hero...!"
onKeyPress={this.handleKeyPress}
/>
<List/>
</div>
);
}
}
export default class List extends React.Component {
constructor() {
super();
this.flipDone = this.flipDone.bind(this);
this.state = {
todos: Array(3).fill({ content: '', done: false})
};
}
flipDone(id) {
let index = Number(id);
this.setState({
todos: [
...this.state.todos.slice(0, index),
Object.assign({}, this.state.todos[index], {done: !this.state.todos[index].done}),
...this.state.todos.slice(index + 1)
]
});
}
render() {
const myList = this.state.todos.map((todo, index) => {
return (
<Todo key={index}
clickHandler={this.flipDone}
id={index}
todo={todo}
handleText={this.handleText}
/>
);
})
return (
<ul className="list">
{myList}
</ul>
);
}
ReactDOM.render(<TodoComp />,document.getElementById('myList'));
You need to make use of refs to call a function in the child component from the parent component
render the List component from parent as
<List ref="myList"/>
and then access the handleNewText() function as this.refs.myList.handleNewText()
UPDATE:
Strings refs are no longer recommended by React, you should rather use ref callbacks, check this
<List ref={(ref) => this.myList=ref}/>
and then access the child function like
this.myList.handleNewText()
Adding to #shubham-khatri solution:
If you are referencing a connected child component...
a. That child must say withRef: true in the (4th) config parameter:
#connect(store => ({
foo: store.whatever
…
}),null,null,{ withRef: true })
b. Access is through getWrappedInstance() (note, that getWrappedInstance also needs to be called ())
getWrappedInstance().howdyPartner()
I started learning React when functional component came out. Another way I experimented with some success is returning functions that you want to access as closures within a JSON. I like this method because closure is a construct of Javascript and it should still work even if React is updated yet again. Below is an example of child component
function Child(){
//declare your states and use effects
const [ppp, setPPP] = useState([]);
const [qqq, setQQQ] = useState(2);
//declare function that you want to access
function funcA(){ /*function to interact with your child components*/}
function funcB(){ /*function to interact with your child components*/}
//pure React functional components here
function Content(){
//function that you cannot access
funcC(){ /*.....*/}
funcD(){/*.......*/}
//what to render
return (
<div>
{/* your contents here */}
</div>
)
}
//return accessible contents and functions in a JSON
return {
content: Content, //function for rendering content
ExposeA: funcA, //return as a closure
ExposeB: funcB, //return as a closure
}
}
Below is an example of how you would render the child contents within the parent
function Parent(){
let chi = Child();
let ChildContent = chi.Content;
//calling your exposed functions
//these function can interacts with the states that affects child components
chi.ExposeA();
chi.ExposeB();
//render your child component
return (<div>
<div> {/* parent stuff here */</div>
<div> {/* parent stuff here */</div>
<ChildContent {/*Define your props here */} />
</div>)
}

React – the right way to pass form element state to sibling/parent elements?

Suppose I have a React class P, which renders two child classes, C1 and C2.
C1 contains an input field. I'll refer to this input field as Foo.
My goal is to let C2 react to changes in Foo.
I've come up with two solutions, but neither of them feels quite right.
First solution:
Assign P a state, state.input.
Create an onChange function in P, which takes in an event and sets state.input.
Pass this onChange to C1 as a props, and let C1 bind this.props.onChange to the onChange of Foo.
This works. Whenever the value of Foo changes, it triggers a setState in P, so P will have the input to pass to C2.
But it doesn't feel quite right for the same reason: I'm setting the state of a parent element from a child element. This seems to betray the design principle of React: single-direction data flow.
Is this how I'm supposed to do it, or is there a more React-natural solution?
Second solution:
Just put Foo in P.
But is this a design principle I should follow when I structure my app—putting all form elements in the render of the highest-level class?
Like in my example, if I have a large rendering of C1, I really don't want to put the whole render of C1 to render of P just because C1 has a form element.
How should I do it?
So, if I'm understanding you correctly, your first solution is suggesting that you're keeping state in your root component? I can't speak for the creators of React, but generally, I find this to be a proper solution.
Maintaining state is one of the reasons (at least I think) that React was created. If you've ever implemented your own state pattern client side for dealing with a dynamic UI that has a lot of interdependent moving pieces, then you'll love React, because it alleviates a lot of this state management pain.
By keeping state further up in the hierarchy, and updating it through eventing, your data flow is still pretty much unidirectional, you're just responding to events in the Root component, you're not really getting the data there via two way binding, you're telling the Root component that "hey, something happened down here, check out the values" or you're passing the state of some data in the child component up in order to update the state. You changed the state in C1, and you want C2 to be aware of it, so, by updating the state in the Root component and re-rendering, C2's props are now in sync since the state was updated in the Root component and passed along.
class Example extends React.Component {
constructor (props) {
super(props)
this.state = { data: 'test' }
}
render () {
return (
<div>
<C1 onUpdate={this.onUpdate.bind(this)}/>
<C2 data={this.state.data}/>
</div>
)
}
onUpdate (data) { this.setState({ data }) }
}
class C1 extends React.Component {
render () {
return (
<div>
<input type='text' ref='myInput'/>
<input type='button' onClick={this.update.bind(this)} value='Update C2'/>
</div>
)
}
update () {
this.props.onUpdate(this.refs.myInput.getDOMNode().value)
}
})
class C2 extends React.Component {
render () {
return <div>{this.props.data}</div>
}
})
ReactDOM.renderComponent(<Example/>, document.body)
Having used React to build an app now, I'd like to share some thoughts to this question I asked half a year ago.
I recommend you to read
Thinking in React
Flux
The first post is extremely helpful to understanding how you should structure your React app.
Flux answers the question why should you structure your React app this way (as opposed to how to structure it). React is only 50% of the system, and with Flux you get to see the whole picture and see how they constitute a coherent system.
Back to the question.
As for my first solution, it is totally OK to let the handler go the reverse direction, as the data is still going single-direction.
However, whether letting a handler trigger a setState in P can be right or wrong depending on your situation.
If the app is a simple Markdown converter, C1 being the raw input and C2 being the HTML output, it's OK to let C1 trigger a setState in P, but some might argue this is not the recommended way to do it.
However, if the app is a todo list, C1 being the input for creating a new todo, C2 the todo list in HTML, you probably want to handler to go two level up than P -- to the dispatcher, which let the store update the data store, which then send the data to P and populate the views. See that Flux article. Here is an example: Flux - TodoMVC
Generally, I prefer the way described in the todo list example. The less state you have in your app the better.
Five years later with introduction of React Hooks there is now much more elegant way of doing it with use useContext hook.
You define context in a global scope, export variables, objects and functions in the parent component and then wrap children in the App in a context provided and import whatever you need in child components. Below is a proof of concept.
import React, { useState, useContext } from "react";
import ReactDOM from "react-dom";
import styles from "./styles.css";
// Create context container in a global scope so it can be visible by every component
const ContextContainer = React.createContext(null);
const initialAppState = {
selected: "Nothing"
};
function App() {
// The app has a state variable and update handler
const [appState, updateAppState] = useState(initialAppState);
return (
<div>
<h1>Passing state between components</h1>
{/*
This is a context provider. We wrap in it any children that might want to access
App's variables.
In 'value' you can pass as many objects, functions as you want.
We wanna share appState and its handler with child components,
*/}
<ContextContainer.Provider value={{ appState, updateAppState }}>
{/* Here we load some child components */}
<Book title="GoT" price="10" />
<DebugNotice />
</ContextContainer.Provider>
</div>
);
}
// Child component Book
function Book(props) {
// Inside the child component you can import whatever the context provider allows.
// Earlier we passed value={{ appState, updateAppState }}
// In this child we need the appState and the update handler
const { appState, updateAppState } = useContext(ContextContainer);
function handleCommentChange(e) {
//Here on button click we call updateAppState as we would normally do in the App
// It adds/updates comment property with input value to the appState
updateAppState({ ...appState, comment: e.target.value });
}
return (
<div className="book">
<h2>{props.title}</h2>
<p>${props.price}</p>
<input
type="text"
//Controlled Component. Value is reverse vound the value of the variable in state
value={appState.comment}
onChange={handleCommentChange}
/>
<br />
<button
type="button"
// Here on button click we call updateAppState as we would normally do in the app
onClick={() => updateAppState({ ...appState, selected: props.title })}
>
Select This Book
</button>
</div>
);
}
// Just another child component
function DebugNotice() {
// Inside the child component you can import whatever the context provider allows.
// Earlier we passed value={{ appState, updateAppState }}
// but in this child we only need the appState to display its value
const { appState } = useContext(ContextContainer);
/* Here we pretty print the current state of the appState */
return (
<div className="state">
<h2>appState</h2>
<pre>{JSON.stringify(appState, null, 2)}</pre>
</div>
);
}
const rootElement = document.body;
ReactDOM.render(<App />, rootElement);
You can run this example in the Code Sandbox editor.
The first solution, with keeping the state in parent component, is the correct one. However, for more complex problems, you should think about some state management library, redux is the most popular one used with react.
I'm surprised that there are no answers with a straightforward idiomatic React solution at the moment I'm writing. So here's the one (compare the size and complexity to others):
class P extends React.Component {
state = { foo : "" };
render(){
const { foo } = this.state;
return (
<div>
<C1 value={ foo } onChange={ x => this.setState({ foo : x })} />
<C2 value={ foo } />
</div>
)
}
}
const C1 = ({ value, onChange }) => (
<input type="text"
value={ value }
onChange={ e => onChange( e.target.value ) } />
);
const C2 = ({ value }) => (
<div>Reacting on value change: { value }</div>
);
I'm setting the state of a parent element from a child element. This seems to betray the design principle of React: single-direction data flow.
Any controlled input (idiomatic way of working with forms in React) updates the parent state in its onChange callback and still doesn't betray anything.
Look carefully at C1 component, for instance. Do you see any significant difference in the way how C1 and built-in input component handle the state changes? You should not, because there is none. Lifting up the state and passing down value/onChange pairs is idiomatic for raw React. Not usage of refs, as some answers suggest.
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.
You should learn Redux and ReactRedux library.It will structure your states and props in one store and you can access them later in your components .
With React >= 16.3 you can use ref and forwardRef, to gain access to child's DOM from its parent. Don't use old way of refs anymore.
Here is the example using your case :
import React, { Component } from 'react';
export default class P extends React.Component {
constructor (props) {
super(props)
this.state = {data: 'test' }
this.onUpdate = this.onUpdate.bind(this)
this.ref = React.createRef();
}
onUpdate(data) {
this.setState({data : this.ref.current.value})
}
render () {
return (
<div>
<C1 ref={this.ref} onUpdate={this.onUpdate}/>
<C2 data={this.state.data}/>
</div>
)
}
}
const C1 = React.forwardRef((props, ref) => (
<div>
<input type='text' ref={ref} onChange={props.onUpdate} />
</div>
));
class C2 extends React.Component {
render () {
return <div>C2 reacts : {this.props.data}</div>
}
}
See Refs and ForwardRef for detailed info about refs and forwardRef.
The right thing to do is to have the state in the parent component, to avoid ref and what not
An issue is to avoid constantly updating all children when typing into a field
Therefore, each child should be a Component (as in not a PureComponent) and implement shouldComponentUpdate(nextProps, nextState)
This way, when typing into a form field, only that field updates
The code below uses #bound annotations from ES.Next babel-plugin-transform-decorators-legacy of BabelJS 6 and class-properties (the annotation sets this value on member functions similar to bind):
/*
© 2017-present Harald Rudell <harald.rudell#gmail.com> (http://www.haraldrudell.com)
All rights reserved.
*/
import React, {Component} from 'react'
import {bound} from 'class-bind'
const m = 'Form'
export default class Parent extends Component {
state = {one: 'One', two: 'Two'}
#bound submit(e) {
e.preventDefault()
const values = {...this.state}
console.log(`${m}.submit:`, values)
}
#bound fieldUpdate({name, value}) {
this.setState({[name]: value})
}
render() {
console.log(`${m}.render`)
const {state, fieldUpdate, submit} = this
const p = {fieldUpdate}
return (
<form onSubmit={submit}> {/* loop removed for clarity */}
<Child name='one' value={state.one} {...p} />
<Child name='two' value={state.two} {...p} />
<input type="submit" />
</form>
)
}
}
class Child extends Component {
value = this.props.value
#bound update(e) {
const {value} = e.target
const {name, fieldUpdate} = this.props
fieldUpdate({name, value})
}
shouldComponentUpdate(nextProps) {
const {value} = nextProps
const doRender = value !== this.value
if (doRender) this.value = value
return doRender
}
render() {
console.log(`Child${this.props.name}.render`)
const {value} = this.props
const p = {value}
return <input {...p} onChange={this.update} />
}
}
The concept of passing data from parent to child and vice versa is explained.
import React, { Component } from "react";
import ReactDOM from "react-dom";
// taken refrence from https://gist.github.com/sebkouba/a5ac75153ef8d8827b98
//example to show how to send value between parent and child
// props is the data which is passed to the child component from the parent component
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
fieldVal: ""
};
}
onUpdateParent = val => {
this.setState({
fieldVal: val
});
};
render() {
return (
// To achieve the child-parent communication, we can send a function
// as a Prop to the child component. This function should do whatever
// it needs to in the component e.g change the state of some property.
//we are passing the function onUpdateParent to the child
<div>
<h2>Parent</h2>
Value in Parent Component State: {this.state.fieldVal}
<br />
<Child onUpdate={this.onUpdateParent} />
<br />
<OtherChild passedVal={this.state.fieldVal} />
</div>
);
}
}
class Child extends Component {
constructor(props) {
super(props);
this.state = {
fieldValChild: ""
};
}
updateValues = e => {
console.log(e.target.value);
this.props.onUpdate(e.target.value);
// onUpdateParent would be passed here and would result
// into onUpdateParent(e.target.value) as it will replace this.props.onUpdate
//with itself.
this.setState({ fieldValChild: e.target.value });
};
render() {
return (
<div>
<h4>Child</h4>
<input
type="text"
placeholder="type here"
onChange={this.updateValues}
value={this.state.fieldVal}
/>
</div>
);
}
}
class OtherChild extends Component {
render() {
return (
<div>
<h4>OtherChild</h4>
Value in OtherChild Props: {this.props.passedVal}
<h5>
the child can directly get the passed value from parent by this.props{" "}
</h5>
</div>
);
}
}
ReactDOM.render(<Parent />, document.getElementById("root"));

Resources