Reactjs - passing state value from one component to another - reactjs

I have two components SideNav and Dashboard (two are in different js files). SideNav will have selectbox as filters. I have to pass an array from Dashboard component to Sidebar component. This array has to given as values for select box (which is inside sidenav component).
P.S. What will be the case if I have two different component classes defined in two different JS files.
e.g. HomeComponent/Home.js -> Parent component
Dashboard/Dashboard.js -> Child component
I am making API call on "Home.js" file and getting some data. I want to pass these data to "Dashboard.js" file (component)
All the examples I studied, they show two components in the same JS file.
class Dashboard extends Component {
constructor(props) {
super(props);
this.state = {viz:{},filterData:{}};
}
var data1= ['1','2','3'];
this.setState({data1: data1}, function () {
console.log(this.state.data1);
});
}
//Sidebar
class Sidebar extends Component {
constructor(props) {
super(props);
this.state = {
data: ['opt1','opt2']
};
}
handleClick(e) {
e.preventDefault();
e.target.parentElement.classList.toggle('open');
this.setState({data: this.state.data1}, function () {
console.log(this.state.data);
});
}
render() {
const props = this.props;
const handleClick = this.handleClick;
return (
<div className="sidebar">
<nav className="sidebar-nav">
<Nav>
<li className="nav-item nav-dropdown">
<p className="nav-link nav-dropdown-toggle" onClick={handleClick.bind(this)}>Global</p>
<ul className="nav-dropdown-items">
<li> Organization <br/>
<select className="form-control">
<option value="">Select </option>
{this.state.data.map(function (option,key) {
return <option key={key}>{option}</option>;
})}
</select>

If you have to pass state from Dashboard to Sidebar, you have to render Sidebar from Dashboard's render function. Here, you can pass the state of Dashboard to Sidebar.
Code snippet
class Dashboard extends Component {
...
...
render(){
return(
<Sidebar data={this.state.data1}/>
);
}
}
If you want the changes made on props (data1) passed to Sidebar be received by Dashboard, you need to lift the state up. i.e, You have to pass a function reference from Dashboard to Sidebar. In Sidebar, you have to invoke it whenever you want the data1 to be passed back to Dashboard.
Code snippet.
class Dashboard extends Component {
constructor(props){
...
//following is not required if u are using => functions in ES6.
this.onData1Changed = this.onData1Changed.bind(this);
}
...
...
onData1Changed(newData1){
this.setState({data1 : newData1}, ()=>{
console.log('Data 1 changed by Sidebar');
})
}
render(){
return(
<Sidebar data={this.state.data1} onData1Changed={this.onData1Changed}/>
);
}
}
class Sidebar extends Component {
...
//whenever data1 change needs to be sent to Dashboard
//note: data1 is a variable available with the changed data
this.props.onData1changed(data1);
}
Reference Doc : https://facebook.github.io/react/docs/lifting-state-up.html

You can only pass props from parent to child component. Either restructure your components hierarchy to have this dependence, or use a state/event management system like Redux (react-redux) .

I had the same issue with parent and child components and the solution was simply send down the function (which is altering the state in the parent component) as a prop to the child component. In this way both are sharing that particular variable's state. Hope this straightforward approach helps you!

I believe keeping the status values aligned with the page URL is another good way, not only to pass values, but also to keep the page status controllable with urls.
Imagine that you are building an advanced search page, where different components will control the search criteria, hence, in addition to search functionality, user should be able to keep his search settings by the used URL.
Supposing that clicking on a link in component x adds a query string criteria1=x to the current page url, and so on for the other components. Let's say we have also configured the search functionality to depend on the URL to read state values from it, this way, you will be able to pass values from a specific component to any number of components without restrictions.

This is called as props drilling.
You can pass data from one component to another by several ways
useContext Hook
Context API
Redux (Its a pattern)
Context with useContext hook, this is a better approach as using Context will increase the code complexity
const themes = {
light: {
foreground: "#000000",
background: "#eeeeee"
},
dark: {
foreground: "#ffffff",
background: "#222222"
}
};
const ThemeContext = React.createContext(themes.light);
function App() {
return (
<ThemeContext.Provider value={themes.dark}>
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme.background,
color: theme.foreground
}}> I am styled by theme context!</button> );
}
For detailed information you can visit the links : useContext , Context and Redux
Redux is better for large-scale application, and if you have multiple Context always go for useContext hook.

Related

Change HOC wrapper component with its own state to react hooks

I currently have an HOC component that I'd like to port over to use react hooks and basically just start thinking in that idea.
This HOC component basically provides a functionality for a wrapped component to display an alert dialog box. The HOC component manages its own state making this very easy for the wrapped component to display an alert dialog. The wrapped component simply has to call the function passed down to props to display it.
Here's what the HOC looks like now:
function withAlertDialog(WrappedComponent) {
return class extends Component {
constructor(props) {
super(props);
this.state = {
alertDialogOpen: false,
alertMessage: ""
};
}
displayAlert = message => {
this.setState({alertDialogOpen: true, alertMessage: message});
}
closeAlertDialog = () => {
this.setState({alertDialogOpen: false});
}
render() {
return (
<React.Fragment>
<WrappedComponent
onDisplayAlert={this.displayAlert}
onCloseAlert={this.closeAlertDialog} />
<MyAlertDialogComponent
open={this.state.alertDialogOpen}
onClose={this.closeAlertDialog} />
</React.Fragment>
);
}
}
}
This is a more simple case, the actual HOC used is a lot more complex, but the idea still follows. The wrapped component can now basically call this.props.onDisplayAlert('some message here'); to display the alert. The wrapped component also doesn't have to render the MyAlertDialogComponent in its own render function. Basically, the wrapped component does not have to worry about how MyAlertDialogComponent is handled, all it knows is that calling this.props.onDisplayAlert will display an alert dialog box somehow. Reusing this HOC saves a lot of lines of code.
How would one go about changing this to a react hooks implementation? I've tried looking around but most articles and the documentation itself use an HOC with a single wrapped component and isn't really managing another component in addition to that. I'd like to understand how to change to the "react hooks" ideology but keep that same level of convenience about not having to render MyAlertDialogComponent in each component that wants to use it.
The only difference between your old HOC and a new HOC utilizing hooks is that you simply have to change the anonymous class you return from your HOC to an anonymous function that uses hooks.
Conversion between a class and a hooked-in function follows normal conversion rules that you might find in numerous tutorials online. In the case of your example, convert your state to useState and convert your class methods to regular functions.
You just pass the state and these regular functions around to whatever component needs them. Calling the setter for your state will re-render the component.
If you review the example below you'll see MyWrappedComponent is wrapped using withAlertDialog which passes the two function props to MyWrappedComponent. Those functions are used inside MyWrappedComponent to set the state that renders MyAlertDialogComponent
const { useState } = React
function withAlertDialog(WrappedComponent) {
return function(props){
const [alertState, setAlertState] = useState({
alertDialogOpen: false,
alertMessage: ""
})
const displayAlert = message => {
setAlertState({
alertDialogOpen: true,
alertMessage: message
});
}
const closeAlertDialog = () => {
setAlertState({alertDialogOpen: false});
}
return (
<React.Fragment>
<WrappedComponent
onDisplayAlert={displayAlert}
onCloseAlert={closeAlertDialog} />
<MyAlertDialogComponent
open={alertState.alertDialogOpen}
onClose={closeAlertDialog} />
</React.Fragment>
);
}
}
const MyWrappedComponent = withAlertDialog(function (props){
return (
<div>
<a onClick={props.onDisplayAlert}>Open Alert</a>
<a onClick={props.onCloseAlert}>Close Alert</a>
</div>
)
})
function MyAlertDialogComponent(props){
if(!props.open){
return null
}
return (
<div>Dialogue Open</div>
)
}
function App(){
return (
<MyWrappedComponent />
)
}
ReactDOM.render(<App />, document.querySelector('#app'))
div > a {
display : block;
padding : 10px 0;
}
<div id="app" />
<script crossorigin src="https://unpkg.com/react#16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script>

React, how to load states/properties from child components

Ok, let's say I have a component like:
Which is formed by 2 different components:
1) The entire rectangle(let's call it card)
2) Each side(the square) is another component(let's call it cardSide)
I added a button on card that when clicked it gathers all the information on each one of the cardSide components(text, note, image, etc).
My questions are, How can I achieve that?
I've read about passing refs from parent to children and sending props from parent to children, but I haven't found any example of the opposite getting the props/states from children components.
I have no much experience on React and I'm using hooks and functions instead of classes(in case that matters) in Java this is very easy to do this by accessing the get methods of each instance, how can be done in React?.
see this url: Call child method from parent. and read the ansewers of rossipedia.
it seems "Using Class Components (>= react#16.4)" section will be more useful for you.
You will need to create a function / method in the parent container that sets the state. From there you can pass it down to the child component which will be able to set the state of its parent.
In order to achieve this communication i suggest that the child (CardSide Component) communicates with the Card Component via Events .
so when the user finish his operation on the card component an event is fired passing all the data to the parent let me show you an example for what i mean :
Card Component
class Card extends Component {
handleCompelete = data => {
//the data here are all the data entered from the child component
//do some sorting using table name
};
render() {
return <CardSide onCompelete={this.handleCompelete} />;
}
}
CardSide Component
class CardComponent extends Component {
render() {
return (
<div>
{/* data here reprensets what you need to transfer to parent component */}
<button onClick={() => this.props.onCompelete(data)} />
</div>
);
}
}
Edit
You cannot access the state of the child component as it is private to it.
Regarding to the props , you can access it but it is ReadOnly that is passed from the parent component but the child component cannot modify it .
Actually there is a way to access the component children (but i see it will complicate your code rather than simplifying it and i do not recommend this )
lets say that this is you app.js
class App extends Component {
constructor() {
super();
this.state = {
name: "React"
};
}
render() {
return (
<div>
<h1>Title</h1>
<Card>
<CardSide someProp="My Child Prop Value" />
</Card>
</div>
);
}
}
as you can see i included CardSide with property with name someProp as a child for Card rathar than inserting it inside Card Component
In the Card Component i accessed the children property as the following :
class Card extends Component {
handleCompelete = data => {
//the data here are all the data entered from the child component
//do some sorting using table name
};
render() {
return <div>
{this.props.children}
{console.log(this.props.children)}
{this.props.children.props.someProp}
</div>;
}
}
and the CardSide Component
class CardSide extends Component {
render() {
return (
<div>
{/* data here reprensets what you need to transfer to parent component */}
<button onClick={() => this.props.onCompelete(data)} >
Hello btn
</button>
</div>
);
}
}
As you can see it will get your structure more complicated and it will be hard to know who is the children for the card component without intensive tracing .
you can see the code in action via this link https://stackblitz.com/edit/react-fxuufw?file=CardSide.jsx

How to know where to add states in react.js

New to react and did not know how to structure a google search for this so decided to ask it here. Was taking a react tutorial and the instructor did this:
#App.js
import React, { Component } from 'react';
import Ninjas from './Ninjas.js'
class App extends Component {
state = {
ninjas : [
{ name: 'Ryu', age:30, belt:'black', id:1 },
{ name: 'Jacy', age:34, belt:'yellow', id:2 },
{ name: 'Ikenna', age:20, belt:'green', id:3 },
{ name: 'Cole', age:50, belt:'red', id:4 }
]
}
render() {
return (
<div className="App">
<p>My First React App</p>
<hr/>
<Ninjas ninjas={ this.state.ninjas } />
</div>
);
}
}
export default App;
#Ninjas.js
import React, { Component } from 'react';
const Ninjas = (props) => {
const { ninjas } = props;
const ninjaList = ninjas.map(ninja => {
return (
<div className="ninja" key={ ninja.id }>
<div>Name: { ninja.name }</div>
<div>Age: { ninja.age }</div>
<div>Belt: { ninja.belt }</div>
<hr/>
</div>
)
})
return(
<div className="ninja-list">
{ ninjaList }
</div>
)
}
export default Ninjas
But then I tried this and it gave the same result:
#App.js
import React, { Component } from 'react';
import Ninjas from './Ninjas.js'
class App extends Component {
render() {
return (
<div className="App">
<p>My First React App</p>
<hr/>
<Ninjas />
</div>
);
}
}
export default App;
#Ninjas.js
class Ninjas extends Component {
state = {
ninjas : [
{ name: 'Ryu', age:30, belt:'black', id:1 },
{ name: 'Jacy', age:34, belt:'yellow', id:2 },
{ name: 'Ikenna', age:20, belt:'green', id:3 },
{ name: 'Cole', age:50, belt:'red', id:4 }
]
}
render() {
const ninjaList = this.state.ninjas.map(ninja => {
return(
<div className="ninja" key={ ninja.id }>
<div>Name: { ninja.name }</div>
<div>Age: { ninja.age }</div>
<div>Belt: { ninja.belt }</div>
<hr/>
</div>
)
})
return (
<div className="ninja-list">
{ ninjaList }
</div>
)
}
}
export default Ninjas
Why did he put the state in the parent App component and not in the nested Ninjas component?
And how do you know when to pass data down as props and not use it as a state in the component that needs the data?
First of all, congratulations on noticing this ;) You're 1 step closer to React Thinking
In your example, it doesn't make a difference whether ninjas state lives in App, or in <Ninjas/> component. It only matters when this app grows more complicated.
Smart Container vs Dumb Component
The tutorial example is building <Ninjas/> as a dumb/presentational component, which is why it did not use class, but was written as a Stateless Functional Component. It is merely used for displaying data in certain way.
But why? Because we might want to reuse <Ninjas/> component with different data set.
In an actual app, most likely you wouldn't hardcode the ninja's data as state. What usually happen is, a smart container (in this case, App) will make API call to backend server to retrieve all the ninja data, then save them as state.
Using the tutorial's structure, you now have the flexibility to:
Pass down ninjas state to other components that might need the data. For example, a <BeltCount/> component that displays the count for each belt color. Not the best example, but the point here is reusability.
<Ninjas> components can be reused as well! Now that it doesn't have any hardcoded state in it, anyone can reuse <Ninjas> by passing down different ninjas props.
In your second example you are passing an undefined state.ninjas it has no effect whatsoever. The reason why your second example works is because you define the state with the props from the first example.
Try to call it like in the first example with const { ninjas } = props and it won't work anymore.
The reason why you would take the first approach is that you can define an arbitrary list of ninjas while in the second one you have always the same ninjas.
I would like to answer the specific part:
how do you know when to pass data down as props and not use it as a state in the component that needs the data?
It probabaly is because the data in the state is being used / manipulated by some other elements as well. Example could be 'sort'/ 'delete' etc.
As a general rule, you should keep your state as local as possible, i.e, where the state data is being used. Think of the concept encapsulation.
Hope that helps.
With the example as is, there isn't any compelling reason for the state to be at the App level. I would expect that as the tutorial progresses and the example gets more complicated (state being changed in some manner and potentially used/displayed by multiple components), that the reasons for the state being where it is will become more clear.
There are two types of components in React: Container Component and Presentation Component.
Container component is the top level component and has the information about state(and other advanced things like Redux store etc.).
Presentation component are only responsible for representing your content.
The instructor has used functional component for your 'Ninjas' class and it accepts props from the top layer. This is the standard practice in React and I would recommend to follow it. As you progress in your learning, you will better understand why only top level component needs to have the knowledge of state. Good luck!

React component interop

I'm new to react and am working through the tutorials. I think I have a grasp on the Multiple Components parent-child explanation, but am struggling to figure out how to provide interop (in state, events, etc) between independent components. So, given the following indepent, stateful components (that have child components, etc):
ReactDOM.render(
<FooBox />,
document.getElementById('foo')
);
and
ReactDOM.render(
<BarBox />,
document.getElementById('bar')
);
Is there a way to interop between FooBox and Barbox, or do I need to nest both under ReactDOM.render? And if I do, will that still "work"?
Use case: onClick of one of Foo's children, I want to display BarBox (with stateful information from FooBars children.
Yes, you should render into a single element so that you have a "single entry point" for the application and its corresponding React DOM. Some parent component can then control whether BarBox is displayed by setting a boolean on its local state.
For example, you could pass an onClick handler via props to FooBox:
class Parent extends React.Component {
displayBarBox () {
this.setState({ displayBarBox: true })
}
render () {
return (
<div>
<FooBox onClick={this.displayBarBox.bind(this)} />
{this.state.displayBarBox &&
<BarBox />}
</div>
)
}
}
And then use this click handler in FooBox:
class FooBox extends React.Component {
render () {
// Use the click handler passed via props
// (simple example)
return <div onClick={this.props.onClick} />
}
}
FooBox.propTypes = {
onClick: PropTypes.func.isRequired
}

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