ReactJS function is not being called - javascript

class A extends React.Component {
constructor(props) {
super(props);
this.state = { obj: this.two }
this.one = this.one.bind(this);
this.two = this.two.bind(this);
}
one() {
console.log("Working...")
}
two() {
return <input type='submit' onClick={this.one} />;
}
render() {
return (
<h1>
As Function:<B two={this.two} />
As Object Property: <B two={this.state.obj} />
</h1>
)
}
}
class B extends React.Component {
render() {
return (
<h1>{this.props.two()}</h1>
)
}
}
ReactDOM.render(<A />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react-dom.min.js"></script>
<div id='app'></div>
Here i am trying to send a function two from component A to component B which returns an input element.On input element i have given onClick which calls function one of component A and outputs in console.
So now i'm sending the function two to component B in two forms
One as a direct function
Other as a property of an object,which is given in state[as object property]
In both the cases button is rendering but the input is firing onClick only with the 1st case but not with 2nd
i want to make this work in 2nd case also.
Thanks in advance...Help me out

Actually, the easiest change would be to move the state assignment to the last line of your constructor, for the simple reason, that then the binding to the this context would work.
class A extends React.Component {
constructor(props) {
super(props);
this.one = this.one.bind(this);
this.two = this.two.bind(this);
this.state = { obj: this.two };
}
// ... rest stays as is
}
When you are setting the state first, and only afterwards change the binding context, then the state.obj won't be context bound to the this.
And although this would be a solution to your problem as you presented it, I am unclear if this is what you really want. I strongly advise against creating child components in the way you did, it would make A way to powerfull, A should be the one deciding how B should be rendered.
Maybe you just want to pass a stateless component to your class B, that receives a callback function that should trigger on class A, which could/should be achieved by passing props, not by passing a method which renders another component.
So you could create something rather like
const SubmitButton = ({
trigger = () => {},
text = "Submit"
}) => <input type="Submit" onClick={trigger} value={text} />;
and then use it like in this example
const SubmitButton = ({
trigger = () => {},
text = "Submit"
}) => <input type="Submit" onClick={trigger} value={text} />;
class A extends React.Component {
constructor(props) {
super(props);
this.state = { obj: this.one }
}
one() {
console.log("Working...")
}
two() {
return <input type='submit' onClick={this.one} />;
}
render() {
return (
<h1>
<div>As Function:<SubmitButton trigger={() => this.one()} text="Submit Function" /></div>
<div>As Object Property: <SubmitButton trigger={() => this.state.obj()} text="Submit state" /></div>
<div>Through B function:<B itemTemplate={SubmitButton} trigger={() => this.one()} /></div>
<div>Through B state:<B itemTemplate={SubmitButton} trigger={() => this.state.obj()} /></div>
</h1>
)
}
}
class B extends React.Component {
render() {
const Template = this.props.itemTemplate;
return <h1><Template {...this.props} /></h1>;
}
}
ReactDOM.render(<A />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react-dom.min.js"></script>
<div id='app'></div>

State is asynchronous in React. You cannot set a state and consume it immmediately. This applies to initial state set too.

Related

Am I not allowed to pass props from outside of the map function?

I'm building an app with React, and getting a TypeError that my function doesn't exist. My function starts in here where I pass it to a child component:
class InvoiceScreen extends Component {
state = {
numberOfInvoices: InvoiceData.length,
currentDisplay: <InvoiceList
openInvoice={this.openInvoice}
/>
};
checkInvoiceLength = () => {
var isEmpty = document.getElementById("InvoiceList").innerHTML === "";
if (isEmpty == false) {
this.setState({display: "untoggled"})
}
else if (isEmpty == true) {
this.setState({hasInvoices: "toggled"})
}
}
openInvoice = (int) => {
this.setState({currentDisplay:
<InvoiceDetails
idNumber={InvoiceData[int].id}
description={InvoiceData[int].description}
street={InvoiceData[int].senderAddress.street}
city={InvoiceData[int].senderAddress.city}
postCode={InvoiceData[int].senderAddress.postCode}
country={InvoiceData[int].senderAddress.country}
createdAt={InvoiceData[int].createdAt}
paymentDue={InvoiceData[int].paymentDue}
clientStreet={InvoiceData[int].clientAddress.street}
clientCity={InvoiceData[int].clientAddress.city}
clientPostCode={InvoiceData[int].clientAddress.postCode}
clientCountry={InvoiceData[int].clientAddress.country}
clientEmail={InvoiceData[int].clientEmail}
items={InvoiceData[int].items}
total={InvoiceData[int].total}
/>})
}
render() {
return(
<div className="InvoiceScreen">
<IconBar />
<div className="DisplayArea">
{this.state.currentDisplay}
</div>
</div>
)
}
}
Then from this component, I pass it down to multiple components created through the map function
class InvoiceList extends Component {
constructor(props) {
super(props)
}
render() {
return(
<div className="InvoiceListScreen">
<InvoiceOptions numberOfInvoices={this.props.numberOfInvoices} />
<div id="InvoiceList">
{InvoiceData.map((invoice, index,) =>
<InvoiceBar
openInvoice={this.props.openInvoice}
key={index}
position={index}
idNumber={invoice.id}
clientName={invoice.clientName}
paymentDue={invoice.paymentDue}
price={Formatter.format(invoice.total)}
status={invoice.status.charAt(0).toUpperCase() + invoice.status.slice(1).toLowerCase()}
/>
)}
</div>
</div>
)
}
}
And then finally inside of the mapped components, I call it as an onClick
class InvoiceBar extends Component {
constructor(props) {
super(props)
}
render() {
return(
<div className="InvoiceBar" onClick={() => this.props.openInvoice(this.props.position)}>
<h4 className="idNumber"><span className="Hashtag">#</span>{this.props.idNumber}</h4>
<p className="clientName">{this.props.clientName}</p>
<div className="DueAndPrice">
<p className="paymentDue">Due {this.props.paymentDue}</p>
<h3 className="price">{this.props.price}</h3>
</div>
<PaymentStatus status={this.props.status} />
</div>
)
}
}
And then like I said, I'm given a TypeError saying that it isn't a function. I'm wondering if it has something to do with the function being passed as props from outside of the map function in the second component. Can someone please enlighten me on what it is I'm doing wrong?
The problem is that class fields run in order in which they're listed. They're not like normal methods, which get defined on the prototype ahead of time. For a simplified version:
class InvoiceScreen extends Component {
state = {
openInvoice: this.openInvoice
};
openInvoice = () => {
// some function
}
desugars to:
class InvoiceScreen extends Component {
constructor(props) {
super(props);
this.state = {
openInvoice: this.openInvoice
};
this.openInvoice = () => {
// some function
}
See the problem? You're defining this.state before you're defining this.openInvoice.
Easiest solution would be to move the definition of this.state to the bottom:
class InvoiceScreen extends Component {
// PUT ALL OTHER METHOD DEFINITIONS HERE
// then just before the end of the component:
state = {
numberOfInvoices: InvoiceData.length,
currentDisplay: <InvoiceList
openInvoice={this.openInvoice}
/>
};
}
That said, putting a React component into state is really, really weird. Consider a different approach if at all possible.

React/ ESLint - JSX props should not use arrow functions

I'm currently creating a component in react and i'm using the ES Lint rule react/jsx-no-bind. My issue here is that I want to be able to pass a parameter to my components function. Here is the code I would like to use to be able to do so:
class LanguageDropdown extends Component {
constructor(props) {
super(props);
this.state = {};
}
changeLanguage = (lang) => {
console.log(lang)
};
render() {
return (
<div>
{this.props.languages.map(lang => <button onCLick={() => this.changeLanguage(lang)}>{lang}</button>)}
</div>
)
}
...
This pulls up the ESlint error:
JSX props should not use arrow functions
I'm not entirely sure how to achieve this without using an arrow function or using .bind(). I could add a data-attribute to the button element and then just pass in the event into the changeLanguage function and fetch the attribute using event.target() but this doesn't feel like it's the way it should be approached in React.
Can someone tell me what would be the correct way?
You can refactor button into its own component:
class MyButton extends Component {
static propTypes = {
language: PropTypes.string.isRequired,
};
onClick = () => console.log(this.props.language);
render() {
const {language} = this.props;
return (
<button onClick={this.onClick} type="submit">
{language}
</button>);
}
}
and then in your LanguageDropDown class, use MyButton like this:
class LanguageDropdown extends Component {
...
render() {
return (
<div>
{this.props.languages.map(lang => <MyButton key={lang} language={lang}/>)}
</div>
)
}
...
}
A couple of additional things:
You have a typo onCLick should be onClick
You need a key for repeated items
try the below code.
here I tried by taking the value into the state, same can be tried using props.
class LanguageDropdown extends Component {
constructor(props) {
super(props);
this.state = {languages:['telugu','hindi','english']};
// this.changeLanguage = this.changeLanguage.bind(this);
}
changeLanguage(event,lang){
//event.preventDefault();
console.log('change lang: '+JSON.stringify(lang));
};
render() {
return (
<div>
{this.state.languages.map(lang => <button onClick={(event)=>this.changeLanguage(event,lang)}>{lang}</button>)}
</div>
)
}
}
render(<LanguageDropdown />, document.getElementById('root'));
when you bind the handler in the onClick event where you are passing the value to the handler, then we have to pass that value from the event and collect it to get that value.

React dynamically rendering component (object assign vs. function return)

I've hit a brick wall here, meaning I can't fully figure out why the next two versions of code behave so differently.
In the first version, when I'm initialising a this.childComponent = (<ChildComp />), its props do not seem to update when I change the Parent's state (via setState()). This happens even though the setState() is actually called, and the Parent's state is updated.
In the second version, when I'm actually initialising a function that returns the component (this.childComponent = () => {return (<ChildComp />)}), everything works like a charm, props are updated.
I am using the second version (since it works), but I'd like to understand why this works and the first one doesn't.
Here's the child component:
class Child extends React.Component {
render() {
return (
<button onClick=(this.props.setValue())>
{this.props.value}
</button>
)
}
}
I have the next two versions of the parent component:
1.
class Parent extends React.Component {
constructor() {
this.state = {
value: 1
}
this.childComponent = (
<Child value={this.state.value}
setValue={() => this.setValue()}/>
)
}
setValue() {
this.setState({value: 2})
}
render () {
return ( {this.childComponent} )
}
}
2. (the this.childComponent is now a function that returns the react Element)
class Parent extends React.Component {
constructor() {
this.state = {
value: 1
}
this.childComponent = () => {
return (
<Child value={this.state.value}
setValue={() => this.setValue()}/>
)
}
}
setValue() {
this.setState({value: 2})
}
render () {
return ( {this.childComponent()} )
}
}
I've tried to simplify the code so my issue is easier to understand.
Thank you in advance
React uses a strategy called reconciliation to efficiently update the DOM every time there's a change in its internal state. Typically, this happens after a setState call.
In your first example, the render method inside the Parent component always returns the same Child component, as it's created only once in the constructor. Because of this, the reconciliation algorithm doesn't find any changes since there aren't any.
I'd like to point out that <Child value={this.state.value} setValue={() => this.setValue()}/> is just syntactic sugar for React.createElement(Child, {value: this.state.value, setValue: () => this.setValue()}, null). createElement simply returns an object.
In your second example, with every render call, you're calling childComponent which in turns create a new Child component.
You don't have a return in the first case since
this.childComponent = (
<Child value={this.state.value}
setValue={() => this.setValue()}/>
)
was defined in the constructor which is executed only once and is a static value now.
whereas it will work as it is a function that is executed everytime it is called.
If you want to go by the first method, define the childcomponent in the render rather than the constructor since render is called on every change. Your code also had a lot of mistakes. See the working snippet below
class Parent extends React.Component {
constructor() {
super();
this.state = {
value: 1
}
}
setValue() {
this.setState({value: 2})
}
render () {
const childComponent = (
<Child value={this.state.value}
setValue={() => this.setValue()}/>
)
return ( <div>{childComponent}</div> )
}
}
class Child extends React.Component {
render() {
return (
<button onClick={this.props.setValue}>
{this.props.value}
</button>
)
}
}
ReactDOM.render(<Parent/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

hand over function to onClick on props with parameters

I encapsulated some HTML code to a extra class and want to hand over a function to it now.
The parent class looks like this:
class Home extends React.Component {
doSomething = id => {
console.log(id);
// here are some fetch operations only available in Home component
};
render() {
return (
<Child doSomething={() => this.doSomething} />
)
}
}
my Child component then looks like this:
const id = 3;
const Child = ({doSomething}) =>
(
<Button onClick={doSomething(id)}>Click</Button>
);
export default Child
I was trying around with different solutions but either I get no result or the onClick function is called when the Home component is rendered instead of when clicking on the button.
I want the function to be executed when the button is clicked. And the id parameter should be handed over as well. I can't have the function in the Child component itself since I have to use some redux actions in it which are not available in the child class.
I know this is not a too difficult question but I'm still a noob with JavaScript..
Edit: I accomplished having the event parameter inside my function but I wonder how to access the id with it. I can't simply add a prop to the Button element since it does not allow that.
Thanks in advance for your help,
Erik
You need to bind method in constructor and pass it to child component
class Home extends React.Component {
constructor() {
this.doSomething = this.doSomething.bind(this);
}
doSomething(id) {
console.log(id);
// here are some fetch operations only available in Home component
}
render() {
return <Child doSomething={this.doSomething} />
}
}
And in Child
const Child = ({doSomething}) =>
(
<Button onClick={() => doSomething(id)}>Click</Button>
)
I think you need something like this:
this.doSomething.bind(this);
It binds this as the first argument of your function, which is needed when you pass a class method as a reference. When doSomething is called in the child component, this will reference the parent component.
First of all your jsx is wrong. Your are missing
render() {
return ...;
}
There is no valid JSX Button
<Button onClick={doSomething(id)}>Click</Button>,
use <button> tag instead.
Here is working example.
const element = <h1>Hello, world</h1>;
class Home extends React.Component {
doSomething = id => {
console.log(id);
// here are some fetch operations only available in Home component
};
render() {
return <Child doSomething={() => this.doSomething('do something input')} />;
}
}
class Child extends React.Component {
constructor(props) {
super(props);
//console.log(props);
}
render() {
return <button onClick={this.props.doSomething.bind(this)}>Click</button>;
}
}
ReactDOM.render(
<Home />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react-dom.js"></script>
<div id="root"></div>
You probably need to pass the id when you are defining the fat arrow function
class Home extends React.Component {
doSomething(id) {
console.log(id);
}
render() {
return <Child doSomething={(id) => this.doSomething(id)} />
}
}
Firstly, in Home component use arrow function as doSomething prop to preserve correct context (alternatively you can use bind in component constructor):
class Home extends React.Component {
doSomething(id) {
console.log(id);
// here are some fetch operations only available in Home component
}
render() {
<Child doSomething={() => this.doSomething()} />
}
}
and then use arrow function that will call passed function with given value as click handler in child component:
<Button onClick={() => doSomething(id)}>Click</Button>

How can I update the parent's state in React?

My structure looks as follows:
Component 1
- |- Component 2
- - |- Component 4
- - - |- Component 5
Component 3
Component 3 should display some data depending on state of Component 5.
Since props are immutable, I can't simply save its state in Component 1 and forward it, right? And yes, I've read about Redux, but I don't want to use it. I hope that it's possible to solve it just with react. Am I wrong?
For child-parent communication you should pass a function setting the state from parent to child, like this
class Parent extends React.Component {
constructor(props) {
super(props)
this.handler = this.handler.bind(this)
}
handler() {
this.setState({
someVar: 'some value'
})
}
render() {
return <Child handler = {this.handler} />
}
}
class Child extends React.Component {
render() {
return <Button onClick = {this.props.handler}/ >
}
}
This way the child can update the parent's state with the call of a function passed with props.
But you will have to rethink your components' structure, because as I understand components 5 and 3 are not related.
One possible solution is to wrap them in a higher level component which will contain the state of both component 1 and 3. This component will set the lower level state through props.
This is how to do it with the new useState hook.
Method - Pass the state changer function as a props to the child component and do whatever you want to do with the function:
import React, {useState} from 'react';
const ParentComponent = () => {
const[state, setState]=useState('');
return(
<ChildComponent stateChanger={setState} />
)
}
const ChildComponent = ({stateChanger, ...rest}) => {
return(
<button onClick={() => stateChanger('New data')}></button>
)
}
I found the following working solution to pass the onClick function argument from the child to the parent component:
Version with passing a method()
//ChildB component
class ChildB extends React.Component {
render() {
var handleToUpdate = this.props.handleToUpdate;
return (<div><button onClick={() => handleToUpdate('someVar')}>
Push me
</button>
</div>)
}
}
//ParentA component
class ParentA extends React.Component {
constructor(props) {
super(props);
var handleToUpdate = this.handleToUpdate.bind(this);
var arg1 = '';
}
handleToUpdate(someArg){
alert('We pass argument from Child to Parent: ' + someArg);
this.setState({arg1:someArg});
}
render() {
var handleToUpdate = this.handleToUpdate;
return (<div>
<ChildB handleToUpdate = {handleToUpdate.bind(this)} /></div>)
}
}
if(document.querySelector("#demo")){
ReactDOM.render(
<ParentA />,
document.querySelector("#demo")
);
}
Look at JSFiddle
Version with passing an Arrow function
//ChildB component
class ChildB extends React.Component {
render() {
var handleToUpdate = this.props.handleToUpdate;
return (<div>
<button onClick={() => handleToUpdate('someVar')}>
Push me
</button>
</div>)
}
}
//ParentA component
class ParentA extends React.Component {
constructor(props) {
super(props);
}
handleToUpdate = (someArg) => {
alert('We pass argument from Child to Parent: ' + someArg);
}
render() {
return (<div>
<ChildB handleToUpdate = {this.handleToUpdate} /></div>)
}
}
if(document.querySelector("#demo")){
ReactDOM.render(
<ParentA />,
document.querySelector("#demo")
);
}
Look at JSFiddle
I want to thank the most upvoted answer for giving me the idea of my own problem basically the variation of it with arrow function and passing param from child component:
class Parent extends React.Component {
constructor(props) {
super(props)
// without bind, replaced by arrow func below
}
handler = (val) => {
this.setState({
someVar: val
})
}
render() {
return <Child handler = {this.handler} />
}
}
class Child extends React.Component {
render() {
return <Button onClick = {() => this.props.handler('the passing value')}/ >
}
}
Hope it helps someone.
I like the answer regarding passing functions around. It's a very handy technique.
On the flip side you can also achieve this using pub/sub or using a variant, a dispatcher, as Flux does. The theory is super simple. Have component 5 dispatch a message which component 3 is listening for. Component 3 then updates its state which triggers the re-render. This requires stateful components, which, depending on your viewpoint, may or may not be an anti-pattern. I'm against them personally and would rather that something else is listening for dispatches and changes state from the very top-down (Redux does this, but it adds additional terminology).
import { Dispatcher } from 'flux'
import { Component } from 'React'
const dispatcher = new Dispatcher()
// Component 3
// Some methods, such as constructor, omitted for brevity
class StatefulParent extends Component {
state = {
text: 'foo'
}
componentDidMount() {
dispatcher.register( dispatch => {
if ( dispatch.type === 'change' ) {
this.setState({ text: 'bar' })
}
}
}
render() {
return <h1>{ this.state.text }</h1>
}
}
// Click handler
const onClick = event => {
dispatcher.dispatch({
type: 'change'
})
}
// Component 5 in your example
const StatelessChild = props => {
return <button onClick={ onClick }>Click me</button>
}
The dispatcher bundles with Flux is very simple. It simply registers callbacks and invokes them when any dispatch occurs, passing through the contents on the dispatch (in the above terse example there is no payload with the dispatch, simply a message id). You could adapt this to traditional pub/sub (e.g., using the EventEmitter from events, or some other version) very easily if that makes more sense to you.
I found the following working solution to pass the onClick function argument from the child to the parent component with a parameter:
Parent class:
class Parent extends React.Component {
constructor(props) {
super(props)
// Bind the this context to the handler function
this.handler = this.handler.bind(this);
// Set some state
this.state = {
messageShown: false
};
}
// This method will be sent to the child component
handler(param1) {
console.log(param1);
this.setState({
messageShown: true
});
}
// Render the child component and set the action property with the handler as value
render() {
return <Child action={this.handler} />
}}
Child class:
class Child extends React.Component {
render() {
return (
<div>
{/* The button will execute the handler function set by the parent component */}
<Button onClick={this.props.action.bind(this,param1)} />
</div>
)
} }
Whenever you require to communicate between a child to the parent at any level down, then it's better to make use of context. In the parent component define the context that can be invoked by the child, such as:
In the parent component, in your case component 3,
static childContextTypes = {
parentMethod: React.PropTypes.func.isRequired
};
getChildContext() {
return {
parentMethod: (parameter_from_child) => this.parentMethod(parameter_from_child)
};
}
parentMethod(parameter_from_child){
// Update the state with parameter_from_child
}
Now in the child component (component 5 in your case), just tell this component that it wants to use the context of its parent.
static contextTypes = {
parentMethod: React.PropTypes.func.isRequired
};
render() {
return(
<TouchableHighlight
onPress = {() => this.context.parentMethod(new_state_value)}
underlayColor='gray' >
<Text> update state in parent component </Text>
</TouchableHighlight>
)}
You can find the Demo project in this GitHub repository.
It seems that we can only pass data from parent to child as React promotes unidirectional data flow, but to make the parent update itself when something happens in its "child component", we generally use what is called a "callback function".
We pass the function defined in the parent to the child as "props" and
call that function from the child triggering it in the parent
component.
class Parent extends React.Component {
handler = (Value_Passed_From_SubChild) => {
console.log("Parent got triggered when a grandchild button was clicked");
console.log("Parent->Child->SubChild");
console.log(Value_Passed_From_SubChild);
}
render() {
return <Child handler = {this.handler} />
}
}
class Child extends React.Component {
render() {
return <SubChild handler = {this.props.handler}/ >
}
}
class SubChild extends React.Component {
constructor(props){
super(props);
this.state = {
somethingImp : [1,2,3,4]
}
}
render() {
return <button onClick = {this.props.handler(this.state.somethingImp)}>Clickme<button/>
}
}
React.render(<Parent />,document.getElementById('app'));
HTML
----
<div id="app"></div>
In this example we can make data pass from sub child → child → parent by passing function to its direct child.
Most of the answers given previously are for React.Component-based designs. If you are using useState in the recent upgrades of the React library, then follow this answer.
I've used a top rated answer from this page many times, but while learning React, I've found a better way to do that, without binding and without an inline function inside props.
Just look here:
class Parent extends React.Component {
constructor() {
super();
this.state = {
someVar: value
}
}
handleChange = (someValue) => {
this.setState({someVar: someValue})
}
render() {
return <Child handler={this.handleChange} />
}
}
export const Child = ({handler}) => {
return <Button onClick={handler} />
}
The key is in an arrow function:
handleChange = (someValue) => {
this.setState({someVar: someValue})
}
You can read more here.
Simply pass the parent's setState function via props to the child component.
function ParentComp() {
const [searchValue, setSearchValue] = useState("");
return <SearchBox setSearchValue={setSearchValue} searchValue={searchValue} />;
}
then in child component:
function SearchBox({ searchValue, setSearchValue }) {
return (
<input
id="search-post"
type="text"
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
placeholder="Search Blogs ..."
/>
)
}
A second example to handle click from child component:
// We've below function and component in parent component
const clickHandler = (val) => {
alert(`httpRequest sent. \nValue Received: ${val}`);
};
// JSX
<HttpRequest clickHandler={clickHandler} />
this is how you get function from parent component then pass a value and fire clickHandler through it.
function HttpRequest({ clickHandler }) {
const [content, setContent] = useState("initialState");
return (
<button onClick={() => clickHandler(content)}>
Send Request
</button>
);
}
export default HttpRequest;
We can create ParentComponent and with a handleInputChange method to update the ParentComponent state. Import the ChildComponent and we pass two props from the parent to the child component i.e., the handleInputChange function and count.
import React, { Component } from 'react';
import ChildComponent from './ChildComponent';
class ParentComponent extends Component {
constructor(props) {
super(props);
this.handleInputChange = this.handleInputChange.bind(this);
this.state = {
count: '',
};
}
handleInputChange(e) {
const { value, name } = e.target;
this.setState({ [name]: value });
}
render() {
const { count } = this.state;
return (
<ChildComponent count={count} handleInputChange={this.handleInputChange} />
);
}
}
Now we create the ChildComponent file and save it as ChildComponent.jsx. This component is stateless because the child component doesn't have a state. We use the prop-types library for props type checking.
import React from 'react';
import { func, number } from 'prop-types';
const ChildComponent = ({ handleInputChange, count }) => (
<input onChange={handleInputChange} value={count} name="count" />
);
ChildComponent.propTypes = {
count: number,
handleInputChange: func.isRequired,
};
ChildComponent.defaultProps = {
count: 0,
};
export default ChildComponent;
If you want to update the parent component,
class ParentComponent extends React.Component {
constructor(props){
super(props);
this.state = {
page: 0
}
}
handler(val){
console.log(val) // 1
}
render(){
return (
<ChildComponent onChange={this.handler} />
)
}
}
class ChildComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
page: 1
};
}
someMethod = (page) => {
this.setState({ page: page });
this.props.onChange(page)
}
render() {
return (
<Button
onClick={() => this.someMethod()}
> Click
</Button>
)
}
}
Here onChange is an attribute with "handler" method bound to its instance. We passed the method handler to the Child class component, to receive via the onChange property in its props argument.
The attribute onChange will be set in a props object like this:
props = {
onChange: this.handler
}
and passed to the child component.
So the child component can access the value of name in the props object like this props.onChange.
It's done through the use of render props.
Now the child component has a button “Click” with an onclick event set to call the handler method passed to it via onChange in its props argument object. So now this.props.onChange in the child holds the output method in the parent class.
Reference and credits: Bits and Pieces
If this same scenario is not spread everywhere you can use React's context, especially if you don't want to introduce all the overhead that state management libraries introduce. Plus, it's easier to learn. But be careful; you could overuse it and start writing bad code. Basically you define a Container component (that will hold and keep that piece of state for you) making all the components interested in writing/reading that piece of data to/from its children (not necessarily direct children).
Context - React
You could also use a plain React properly instead.
<Component5 onSomethingHappenedIn5={this.props.doSomethingAbout5} />
Pass doSomethingAbout5 up to Component 1:
<Component1>
<Component2 onSomethingHappenedIn5={somethingAbout5 => this.setState({somethingAbout5})}/>
<Component5 propThatDependsOn5={this.state.somethingAbout5}/>
<Component1/>
If this is a common problem, you should starting thinking moving the whole state of the application to somewhere else. You have a few options, the most common are:
Redux
Flux
Basically, instead of managing the application state in your component you send commands when something happens to get the state updated. Components pull the state from this container as well so all the data is centralized. This doesn't mean you can't use local state any more, but that's a more advanced topic.
We can set the parent state from a child component by passing a function into the child component as props as below:
class Parent extends React.Component{
state = { term : ''}
onInputChange = (event) => {
this.setState({term: event.target.value});
}
onFormSubmit = (event) => {
event.preventDefault();
this.props.onFormSubmit(this.state.term);
}
render(){
return (
<Child onInputChange={this.onInputChange} onFormSubmit=
{this.onFormSubmit} />
)
}
}
class Child extends React.Component{
render(){
return (
<div className="search-bar ui segment">
<form className="ui form" onSubmit={this.props.onFormSubmit}>
<div class="field">
<label>Search Video</label>
<input type="text" value={this.state.term} onChange=
{this.props.onInputChange} />
</div>
</form>
</div>
)
}
}
This way, the child will update the parent state onInputChange and onFormSubmit are props passed from parents. This can be called from event listeners in the child, hence the state will get updated there.
Parent Component
function Parent() {
const [value, setValue] = React.useState("");
function handleChange(newValue) {
setValue(newValue);
}
// We pass a callback to Child
return <Child value={value} onChange={handleChange} />;
}
Child Component
function Child(props) {
function handleChange(event) {
// Here, we invoke the callback with the new value
props.onChange(event.target.value);
}
return <input value={props.value} onChange={handleChange} />
}
Here is a short snippet to get two ways binding data.
The counter show the value from the parent and is updated from the child
class Parent extends React.Component {
constructor(props) {
super(props)
this.handler = this.handler.bind(this)
this.state = {
count: 0
}
}
handler() {
this.setState({
count: this.state.count + 1
})
}
render() {
return <Child handler={this.handler} count={this.state.count} />
}
}
class Child extends React.Component {
render() {
return <button onClick={this.props.handler}>Count {this.props.count}</button>
}
}
ReactDOM.render(<Parent />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
This is the way I do it:
type ParentProps = {}
type ParentState = { someValue: number }
class Parent extends React.Component<ParentProps, ParentState> {
constructor(props: ParentProps) {
super(props)
this.state = { someValue: 0 }
this.handleChange = this.handleChange.bind(this)
}
handleChange(value: number) {
this.setState({...this.state, someValue: value})
}
render() {
return <div>
<Child changeFunction={this.handleChange} defaultValue={this.state.someValue} />
<p>Value: {this.state.someValue}</p>
</div>
}
}
type ChildProps = { defaultValue: number, changeFunction: (value: number) => void}
type ChildState = { anotherValue: number }
class Child extends React.Component<ChildProps, ChildState> {
constructor(props: ChildProps) {
super(props)
this.state = { anotherValue: this.props.defaultValue }
this.handleChange = this.handleChange.bind(this)
}
handleChange(value: number) {
this.setState({...this.state, anotherValue: value})
this.props.changeFunction(value)
}
render() {
return <div>
<input onChange={event => this.handleChange(Number(event.target.value))} type='number' value={this.state.anotherValue}/>
</div>
}
}
As per your question, I understand that you need to display some conditional data in Component 3 which is based on the state of Component 5. Approach:
The state of Component 3 will hold a variable to check whether Component 5's state has that data
An arrow function which will change Component 3's state variable.
Passing an arrow function to Component 5 with props.
Component 5 has an arrow function which will change Component 3's state variable
An arrow function of Component 5 called on loading itself
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Class Component3 extends React.Component {
state = {
someData = true
}
checkForData = (result) => {
this.setState({someData : result})
}
render() {
if(this.state.someData) {
return(
<Component5 hasData = {this.checkForData} />
//Other Data
);
}
else {
return(
//Other Data
);
}
}
}
export default Component3;
class Component5 extends React.Component {
state = {
dataValue = "XYZ"
}
checkForData = () => {
if(this.state.dataValue === "XYZ") {
this.props.hasData(true);
}
else {
this.props.hasData(false);
}
}
render() {
return(
<div onLoad = {this.checkForData}>
//Conditional Data
</div>
);
}
}
export default Component5;
To set state of parent in the child you can use callback.
const Child = ({handleClick}) => (
<button on click={() => handleClick('some vale')}>change value</button>
)
const parent = () => {
const [value, setValue] = useState(null)
return <Child handleClick={setValue} />
}
In your structure it seems Components 1 an 3 are brothers. So you has 3 options:
1- Put the state into the parent of them(not recommended for 4 layer parent-child).
2- Use useContext and useRducer(or useState) together.
3- Use state managers like redux, mobx ...
This seem to work for me
Parent:
...
const [open, setOpen] = React.useState(false);
const handleDrawerClose = () => {
setOpen(false);
};
...
return (
<PrimaryNavigationAccordion
handleDrawerClose={handleDrawerClose}
/>
);
Child:
...
export default function PrimaryNavigationAccordion({
props,
handleDrawerClose,
})
...
<Link
to={menuItem.url}
component={RouterLink}
color="inherit"
underline="hover"
onClick={() => handleDrawerClose()}
>
{menuItem.label}
</Link>
You can do it by passing a reference for the parent to child, as:
Having a parent component A in A.js with a method updateAState
Having a child component B in B.js
Having a wrapper function that renders <A><B></B></A> in C.js
In C.js you can use useRef as following:
import React, { useRef } from "react";
export default function C()
{
const parentARef = useRef();
const handleChildBClick = () => parentARef.current.updateAState();
return (
<A ref={parentARef}>
<B onClick={handleChildBClick}>
</B>
</A>
);
}
Guidance Reference: https://stackoverflow.com/a/56496607/1770571
Data cannot be passed from child to parent in React. Data must be passed from parent to child. In this case, you can use either the built-in Context API or a third-party state management solution such as Redux, Mobx, or Apollo GraphQL. However, if your app structure is too small, you can store your data in your parent element and then send it to your child via prop drilling. But if your project is larger, it will be messy.
<Footer
action={()=>this.setState({showChart: true})}
/>
<footer className="row">
<button type="button" onClick={this.props.action}>Edit</button>
{console.log(this.props)}
</footer>
Try this example to write inline setState, it avoids creating another function.

Categories