React passing parameter with arrow function in child component - reactjs

I have these parent and child component, I want to pass click function to select an item in child component. Yet it seems the function in child component become automatically called instead of waiting until the user click the element. To make it clearer here is my parent and child components
export class ParentView extends Component {
state = {
selectedItem: {}
}
handleClick = (item) => {
alert('you click me');
this.setState({selectedItem: item});
}
render() {
let item = { name: 'Item-1' };
return (
<div>
<ChildItem item={item} handleClick={this.handleClick} />
</div>
);
}
}
export class ChildItem extends Component {
render() {
const {item, handleClick} = this.props;
return (
<div>
<a onClick={handleClick(item)} />
</div>
);
}
}
Those are my components using arrow function to pass handleClick to child component, yet alert always being called at first render without being triggered by user. Any suggestion?

You should pass a function itself to onClick, not a result of the passed function invocation.
If you would like to invoke it with param, you have options:
bind it with item with handleClick.bind(this, item). bind creates a new function will have a predefined first parameter - item
pass new arrow function like () => handleClick(item)
An example below:
export class ChildItem extends Component {
render() {
const { item, handleClick } = this.props;
return (
<div>
<a onClick={() => handleClick(item)} />
</div>
)
}
}
In your code you're invoking a function in onClick declaration, so the result of handleClick execution will be passed to onClick, what is most likely not something you wanted to achieve.
<a onClick={handleClick(item)} />
Update:
as #dhilt wrote, there is a drawback of such approach. Since the newly created arrow function and .bind also creates new function every time the render method of ChildItem is invoked, react will threat the resulted react element as a different, comparing to the previous "cached" result of render method, that means that likely it might lead to some performance problems in the future, there is even a rule regarding this problem for eslint, but you shouldn't just follow this rule because of two points.
1) performance problems should be measured. we don't forbid using Array.prototype.forEach in favor of a regular for because for is the same or "faster".
2) definition of click handlers as class properties leads to increasing of the initializing step of the component instance. Re-render is fast and efficient in react, so sometimes the initial rendering is more important.
Just use what's better for you and likely read articles like this https://cdb.reacttraining.com/react-inline-functions-and-performance-bdff784f5578

Accepted answer has a performance hit: ChildItem component will be re-rendered even if data hasn’t changed because each render allocates a new function (it is so because of .bind; same with arrow functions). In this particular case it is very easy to avoid such a problem by getting handler and its argument right from the props on new public class field:
export class ChildItem extends Component {
onClick = () => {
this.props.handleClick(this.props.item);
}
render() {
return (
<div>
<a onClick={this.onClick} />
</div>
);
}
}
ParentView remains untouched.

The ES6 way:
Using arrow functions =>
onClick={() => handleClick(item)}
(#havenchyk's answer is the ES5 way).

Related

Which object owns react click handler code?

I know that when using React class methods arrow functions are a great way to lock in this. For example I might have a click handler:
clickHandler = () => {
// some code using 'this'
// eg this.state
}
// ... and in the render method
<button onClick={() => this.clickHandler()}>Test Button</button>
However if I were to use a non-arrow function syntax what object owns the code? That is, what is this?
I have tried the following but Typescript will not compile:
clickHandler = function() {
console.log(this); // also tried to coerce this as any type
}
// Error: 'this' implicitly has type 'any' because it does not have a type annotation.ts(2683)
It depends on how you call the function and whether or not you bind it.
class Test extends React.Component {
clickHandler = function() {
console.log(this);
}
render() {
return (
<div>
<button onClick={this.clickHandler}>Option A</button>
<button onClick={this.clickHandler.bind(this)}>Option B</button>
<button onClick={() => this.clickHandler()}>Option C</button>
</div>
);
}
}
With the Option A button, the click handler is passed directly to the <button /> element. Since React assumes you bind this and won't do it for you, the this inside clickHandler will be undefined.
With the Option B button we're explicitly binding this.clickHandler to this, in which case the this inside clickHandler will refer to that specific instance of the Test component.
For Option C (which is what you're doing in your question) we are not setting this.clickHandler as the onClick handler, but we're defining a new arrow function that calls this.clickHandler. In this case, the new arrow function will automatically get the Test component as this and that one is passed along to clickHandler.
Note that the Option A version will allow components to explicitly bind the handler:
class Button extends React.Component {
render() {
return (
<button
onClick={this.props.onClick.bind(this)}
children={this.props.children}
/>
);
}
}
class Test extends React.Component {
clickHandler = function() {
console.log(this);
}
render() {
return <Button onClick={this.clickHandler}>Click</Button>;
}
}
In this case, this within Test.clickHandler() will actually be a reference to the Button instance.
Your "this" will be 'undefined' because React calls your function behind the scenes and it doesn't call it from the object instance (I.E MyComponent.onClickHandler(e)) it calls it like this (onClickHandler(e))
A simplified model of what's happening without React involved looks something like this.....
class FakeReactComponent {
myName() {
console.log(this);
}
}
// this is what you give a onClick prop on an element.
const functionPassedToOnClick= new FakeReactComponent().myName;
console.log(
functionPassedToOnClick() // this is how react calls it (this is undefined)
)
On method to get around this is to bind "this" of the function in the constructor of the component I.E
constructor(props: IProps) {
super(props);
this.onClickHandler = this.onClickHandler.bind(this);
}
This is actually the 'best practice' way to define all functions within a class for React Components as arrow functions don't go on the classes prototype and therefore fresh functions are created for every new React instance of that component; however with non-arrow functions each instance goes on the prototype and therefore reused for each react component instance.
Tricks like autobind are also not best practice because binding every function's this is unneccessary because only functions being called by React need to have their this bound and 99% of the time that's only functions being used as event handlers.

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

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>
)
}
}

Correct way to pass functions to handlers in 'dumb' components in React

Looking at this simple example where the prop toggleData would be a redux thunk action mapped to the container props.
Is this the recommended way to pass a function like this to a child 'dumb' component? I read an article online saying that using arrow functions inside handlers is expensive and not very efficient from a performance perspective.
class Container extends React.Component {
render(){
return (
<Home toggleData={this.props.toggleData}/>
)
}
}
const Home = (props) => {
return (
<button onClick={()=>{props.toggleData()}}></button>
)
}
Yes! Avoid using arrow functions inside the JSX. Because the function will be created on every render, this might lead to performance issues later on.
If you don't need to send parameters you can do something like this:
const Home = (props) => {
return (
<button onClick={props.toggleData}></button>
)
}
If you need to use parameters, I usually define a class to create the callback using an arrow function, this way it gets created and bound only once.
class Home extends PureComponent {
onToggle = () => {
this.props.toggleData(1, 2, 3);
}
render() {
return (
<button onClick={this.onToggle}></button>
)
}
}

ReactJS: Warning: setState(...): Cannot update during an existing state transition

I am trying to refactor the following code from my render view:
<Button href="#" active={!this.state.singleJourney} onClick={this.handleButtonChange.bind(this,false)} >Retour</Button>
to a version where the bind is within the constructor. The reason for that is that bind in the render view will give me performance issues, especially on low end mobile phones.
I have created the following code, but I am constantly getting the following errors (lots of them). It looks like the app gets in a loop:
Warning: setState(...): Cannot update during an existing state transition (such as within `render` or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to `componentWillMount`.
Below is the code I use:
var React = require('react');
var ButtonGroup = require('react-bootstrap/lib/ButtonGroup');
var Button = require('react-bootstrap/lib/Button');
var Form = require('react-bootstrap/lib/Form');
var FormGroup = require('react-bootstrap/lib/FormGroup');
var Well = require('react-bootstrap/lib/Well');
export default class Search extends React.Component {
constructor() {
super();
this.state = {
singleJourney: false
};
this.handleButtonChange = this.handleButtonChange.bind(this);
}
handleButtonChange(value) {
this.setState({
singleJourney: value
});
}
render() {
return (
<Form>
<Well style={wellStyle}>
<FormGroup className="text-center">
<ButtonGroup>
<Button href="#" active={!this.state.singleJourney} onClick={this.handleButtonChange(false)} >Retour</Button>
<Button href="#" active={this.state.singleJourney} onClick={this.handleButtonChange(true)} >Single Journey</Button>
</ButtonGroup>
</FormGroup>
</Well>
</Form>
);
}
}
module.exports = Search;
Looks like you're accidentally calling the handleButtonChange method in your render method, you probably want to do onClick={() => this.handleButtonChange(false)} instead.
If you don't want to create a lambda in the onClick handler, I think you'll need to have two bound methods, one for each parameter.
In the constructor:
this.handleButtonChangeRetour = this.handleButtonChange.bind(this, true);
this.handleButtonChangeSingle = this.handleButtonChange.bind(this, false);
And in the render method:
<Button href="#" active={!this.state.singleJourney} onClick={this.handleButtonChangeSingle} >Retour</Button>
<Button href="#" active={this.state.singleJourney} onClick={this.handleButtonChangeRetour}>Single Journey</Button>
I am giving a generic example for better understanding, In the following code
render(){
return(
<div>
<h3>Simple Counter</h3>
<Counter
value={this.props.counter}
onIncrement={this.props.increment()} <------ calling the function
onDecrement={this.props.decrement()} <-----------
onIncrementAsync={this.props.incrementAsync()} />
</div>
)
}
When supplying props I am calling the function directly, this wold have a infinite loop execution and would give you that error, Remove the function call everything works normally.
render(){
return(
<div>
<h3>Simple Counter</h3>
<Counter
value={this.props.counter}
onIncrement={this.props.increment} <------ function call removed
onDecrement={this.props.decrement} <-----------
onIncrementAsync={this.props.incrementAsync} />
</div>
)
}
That usually happens when you call
onClick={this.handleButton()} - notice the () instead of:
onClick={this.handleButton} - notice here we are not calling the function when we initialize it
THE PROBLEM is here: onClick={this.handleButtonChange(false)}
When you pass this.handleButtonChange(false) to onClick, you are actually calling the function with value = false and setting onClick to the function's return value, which is undefined. Also, calling this.handleButtonChange(false) then calls this.setState() which triggers a re-render, resulting in an infinite render loop.
THE SOLUTION is to pass the function in a lambda: onClick={() => this.handleButtonChange(false)}. Here you are setting onClick to equal a function that will call handleButtonChange(false) when the button is clicked.
The below example may help:
function handleButtonChange(value){
console.log("State updated!")
}
console.log(handleButtonChange(false))
//output: State updated!
//output: undefined
console.log(() => handleButtonChange(false))
//output: ()=>{handleButtonChange(false);}
If you are trying to add arguments to a handler in recompose, make sure that you're defining your arguments correctly in the handler. It is essentially a curried function, so you want to be sure to require the correct number of arguments. This page has a good example of using arguments with handlers.
Example (from the link):
withHandlers({
handleClick: props => (value1, value2) => event => {
console.log(event)
alert(value1 + ' was clicked!')
props.doSomething(value2)
},
})
for your child HOC and in the parent
class MyComponent extends Component {
static propTypes = {
handleClick: PropTypes.func,
}
render () {
const {handleClick} = this.props
return (
<div onClick={handleClick(value1, value2)} />
)
}
}
this avoids writing an anonymous function out of your handler to patch fix the problem with not supplying enough parameter names on your handler.
The problem is certainly the this binding while rending the button with onClick handler. The solution is to use arrow function while calling action handler while rendering. Like this:
onClick={ () => this.handleButtonChange(false) }
From react docs Passing arguments to event handlers
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
This same warning will be emitted on any state changes done in a render() call.
An example of a tricky to find case:
When rendering a multi-select GUI component based on state data, if state has nothing to display, a call to resetOptions() is considered state change for that component.
The obvious fix is to do resetOptions() in componentDidUpdate() instead of render().
I got the same error when I was calling
this.handleClick = this.handleClick.bind(this);
in my constructor when handleClick didn't exist
(I had erased it and had accidentally left the "this" binding statement in my constructor).
Solution = remove the "this" binding statement.
The onClick function must pass through a function that returns the handleButtonChange() method. Otherwise it will run automatically, ending up with the error/warning. Use the below to solve the issue.
onClick={() => this.handleButtonChange(false)}
The solution that I use to open Popover for components is reactstrap (React Bootstrap 4 components).
class Settings extends Component {
constructor(props) {
super(props);
this.state = {
popoversOpen: [] // array open popovers
}
}
// toggle my popovers
togglePopoverHelp = (selected) => (e) => {
const index = this.state.popoversOpen.indexOf(selected);
if (index < 0) {
this.state.popoversOpen.push(selected);
} else {
this.state.popoversOpen.splice(index, 1);
}
this.setState({ popoversOpen: [...this.state.popoversOpen] });
}
render() {
<div id="settings">
<button id="PopoverTimer" onClick={this.togglePopoverHelp(1)} className="btn btn-outline-danger" type="button">?</button>
<Popover placement="left" isOpen={this.state.popoversOpen.includes(1)} target="PopoverTimer" toggle={this.togglePopoverHelp(1)}>
<PopoverHeader>Header popover</PopoverHeader>
<PopoverBody>Description popover</PopoverBody>
</Popover>
<button id="popoverRefresh" onClick={this.togglePopoverHelp(2)} className="btn btn-outline-danger" type="button">?</button>
<Popover placement="left" isOpen={this.state.popoversOpen.includes(2)} target="popoverRefresh" toggle={this.togglePopoverHelp(2)}>
<PopoverHeader>Header popover 2</PopoverHeader>
<PopoverBody>Description popover2</PopoverBody>
</Popover>
</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