I am trying to make a component which receiving a function as a props. i want to pass some value into function while calling it:
class Course extends Component {
render() {
return (
<div>
<div id="courses">
<p onClick={this.props.sumPrice}>{this.props.name}<b>{this.props.price}</b></p>
</div>
</div>
);
}
}
sumPrice is a function define in parent component and its need a value.
This is my sumPrice function and parent class constructor code:
constructor(props) {
super(props);
this.state = {
active: false,
total: 0
};
this.sumPrice = this.sumPrice.bind(this);
}
sumPrice(price) {
this.setState({ total: this.state.total + price });
}
Usually the closure, arrow function in render handles such situations exactly how it is needed:
<div id="courses">
<p
onClick={() => this.props.sumPrice(this.props.price)}
>
{ this.props.name }<b>{ this.props.price }</b>
</p>
</div>
While it works as expected, unfortunately it comes at cost ot performance penalty Why shouldn't JSX props use arrow functions or bind?. The impact does not have to be dramatic problem but should be generally avoided.
The optimal solution is to use the functions which are not recreated on each re-render, like class method:
class Course extends Component {
constructor(props) {
super(props)
this.onClick = this.onClick.bind(this)
}
onClick () {
const { sumPrice, price } = this.props
sumPrice(price)
}
render() {
return (
<div>
<div id="courses">
<p onClick={this.onClick}>{this.props.name}<b>{this.props.price}</b></p>
</div>
</div>
)
}
}
Avoiding performance issues.
Related
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.
So I started converting my application from ES2015 to ES6 which uses React.
I have a parent class and a child class like so,
export default class Parent extends Component {
constructor(props) {
super(props);
this.state = {
code: ''
};
}
setCodeChange(newCode) {
this.setState({code: newCode});
}
login() {
if (this.state.code == "") {
// Some functionality
}
}
render() {
return (
<div>
<Child onCodeChange={this.setCodeChange} onLogin={this.login} />
</div>
);
}
}
Child class,
export default class Child extends Component {
constructor(props) {
super(props);
}
handleCodeChange(e) {
this.props.onCodeChange(e.target.value);
}
login() {
this.props.onLogin();
}
render() {
return (
<div>
<input name="code" onChange={this.handleCodeChange.bind(this)}/>
</div>
<button id="login" onClick={this.login.bind(this)}>
);
}
}
Child.propTypes = {
onCodeChange: React.PropTypes.func,
onLogin: React.PropTypes.func
};
However this causes the following error,
this.state is undefined
It refers to,
if (this.state.code == "") {
// Some functionality
}
Any idea what could be causing this ?
You can use arrow function to bind you functions. You need to bind you functions both in child as well as parent components.
Parent:
export default class Parent extends Component {
constructor(props) {
super(props);
this.state = {
code: ''
};
}
setCodeChange = (newCode) => {
this.setState({code: newCode});
}
login = () => {
if (this.state.code == "") {
// Some functionality
}
}
render() {
return (
<div>
<Child onCodeChange={this.setCodeChange} onLogin={this.login} />
</div>
);
}
}
Child
export default class Child extends Component {
constructor(props) {
super(props);
}
handleCodeChange = (e) => {
this.props.onCodeChange(e.target.value);
}
login = () => {
this.props.onLogin();
}
render() {
return (
<div>
<input name="code" onChange={this.handleCodeChange}/>
</div>
<button id="login" onClick={this.login}>
);
}
}
Child.propTypes = {
onCodeChange: React.PropTypes.func,
onLogin: React.PropTypes.func
};
There are other ways to bind the functions as well such as the one you are using but you need to do that for parent component too as <Child onCodeChange={this.setCodeChange.bind(this)} onLogin={this.login.bind(this)} />
or you can specify binding in the constructor as
Parent:
constructor(props) {
super(props);
this.state = {
code: ''
};
this.setCodeChange = this.setCodeChange.bind(this);
this.login = this.login.bind(this);
}
Child
constructor(props) {
super(props);
this.handleCodeChange = this.handleCodeChange.bind(this);
this.login = this.login.bind(this);
}
I agree with all different solutions given by #Shubham Kathri except direct binding in render.
You are not recommended to bind your functions directly in render. You are recommended to bind it in constructor always because if you do binding directly in render then whenever your component renders Webpack will create a new function/object in bundled file thus the Webpack bundle file size grows. For many reasons your component re-renders eg: doing setState but if you place it in constructor it gets called called only once.
The below implementation is not recommended
<Child onCodeChange={this.setCodeChange.bind(this)} onLogin={this.login.bind(this)} />
Do it in constructor always and use the ref wherever required
constructor(props){
super(props);
this.login = this.login.bind(this);
this.setCodeChange = this.setCodeChange.bind(this);
}
<Child onCodeChange={this.setCodeChange} onLogin={this.login} />
If you are using ES6 then manual binding is not required but if you want you can. You can use arrow functions if you want to stay away with scope related issues and manual function/object bindings.
Sorry if there are any typos I am answering in my mobile
So I started converting my application from ES2015 to ES6 which uses React.
I have a parent class and a child class like so,
export default class Parent extends Component {
constructor(props) {
super(props);
this.state = {
code: ''
};
}
setCodeChange(newCode) {
this.setState({code: newCode});
}
login() {
if (this.state.code == "") {
// Some functionality
}
}
render() {
return (
<div>
<Child onCodeChange={this.setCodeChange} onLogin={this.login} />
</div>
);
}
}
Child class,
export default class Child extends Component {
constructor(props) {
super(props);
}
handleCodeChange(e) {
this.props.onCodeChange(e.target.value);
}
login() {
this.props.onLogin();
}
render() {
return (
<div>
<input name="code" onChange={this.handleCodeChange.bind(this)}/>
</div>
<button id="login" onClick={this.login.bind(this)}>
);
}
}
Child.propTypes = {
onCodeChange: React.PropTypes.func,
onLogin: React.PropTypes.func
};
However this causes the following error,
this.state is undefined
It refers to,
if (this.state.code == "") {
// Some functionality
}
Any idea what could be causing this ?
You can use arrow function to bind you functions. You need to bind you functions both in child as well as parent components.
Parent:
export default class Parent extends Component {
constructor(props) {
super(props);
this.state = {
code: ''
};
}
setCodeChange = (newCode) => {
this.setState({code: newCode});
}
login = () => {
if (this.state.code == "") {
// Some functionality
}
}
render() {
return (
<div>
<Child onCodeChange={this.setCodeChange} onLogin={this.login} />
</div>
);
}
}
Child
export default class Child extends Component {
constructor(props) {
super(props);
}
handleCodeChange = (e) => {
this.props.onCodeChange(e.target.value);
}
login = () => {
this.props.onLogin();
}
render() {
return (
<div>
<input name="code" onChange={this.handleCodeChange}/>
</div>
<button id="login" onClick={this.login}>
);
}
}
Child.propTypes = {
onCodeChange: React.PropTypes.func,
onLogin: React.PropTypes.func
};
There are other ways to bind the functions as well such as the one you are using but you need to do that for parent component too as <Child onCodeChange={this.setCodeChange.bind(this)} onLogin={this.login.bind(this)} />
or you can specify binding in the constructor as
Parent:
constructor(props) {
super(props);
this.state = {
code: ''
};
this.setCodeChange = this.setCodeChange.bind(this);
this.login = this.login.bind(this);
}
Child
constructor(props) {
super(props);
this.handleCodeChange = this.handleCodeChange.bind(this);
this.login = this.login.bind(this);
}
I agree with all different solutions given by #Shubham Kathri except direct binding in render.
You are not recommended to bind your functions directly in render. You are recommended to bind it in constructor always because if you do binding directly in render then whenever your component renders Webpack will create a new function/object in bundled file thus the Webpack bundle file size grows. For many reasons your component re-renders eg: doing setState but if you place it in constructor it gets called called only once.
The below implementation is not recommended
<Child onCodeChange={this.setCodeChange.bind(this)} onLogin={this.login.bind(this)} />
Do it in constructor always and use the ref wherever required
constructor(props){
super(props);
this.login = this.login.bind(this);
this.setCodeChange = this.setCodeChange.bind(this);
}
<Child onCodeChange={this.setCodeChange} onLogin={this.login} />
If you are using ES6 then manual binding is not required but if you want you can. You can use arrow functions if you want to stay away with scope related issues and manual function/object bindings.
Sorry if there are any typos I am answering in my mobile
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.
I'm building my first React app and am seemingly way over my head. Anyway, I'm trying to take a component like this:
export default class Timer extends Component {
constructor(props) {
super(props)
this.state = { clock: 0, time: '' }
}
componentDidMount() {
this.play()
}
componentWillUnmount() {
this.pause()
}
pause() {
if (interval) {
clearInterval(interval)
interval = null
}
}
render() {
return (
<div className="react-timer" pause={this.pause.bind(this)}>
<h3 className="seconds"> {this.state.time} {this.props.prefix}</h3>
<br />
</div>
)
}
}
Timer.propTypes = {
options: PropTypes.object
}
and access it's state and pause functions in another component that is it's parent because the timer is embedded in the other component.
Other component:
class Level1 extends Component {
constructor(props) {
super(props);
this.state = {x: 0, y: 0};
}
render () {
...
return (
<div>
<div ref="elem" onMouseMove={this._onMouseMove.bind(this)} id="gameBoard">
<img id="waldo1" src={require('../images/waldo1(1).jpg')} alt="waldo"/>
<h2> {x} , {y}</h2>
</div>
<button onClick={this.refs.mytimer.pause()}>Pause</button>
<Timer ref="mytimer" options={OPTIONS}/> <-- Here Timer
</div>
) // return
} // render
} //component
For example, I'm going to write a function like this:
var isWaldoFound = function (x , y ) {
if (true) {
Timer.pause()
hashHistory.push({ '/result' + this.Timer.state})
} else {
..Whatever..
}
}
I've tried using refs and props but when I log anything it's undefined. When I log timer it shows me the Timer but when I log Timer.pause or Timer.pause() it says it is undefined.
How can I go about doing this?
React is all about your UI being a function of your state; i.e., React is all about state. So, instead of doing things imperatively, you change your state and the components "react" to the change in state.
You might want to read:
https://zhenyong.github.io/react/docs/more-about-refs.html
and
https://facebook.github.io/react/docs/refs-and-the-dom.html
In your case, you might pass the "current state" as a prop to the Timer, and then in componentWillRecieveProps() in the Timer, check the new "current state" against the current one, and if there's a change in state, then have the timer transition itself to the new "current state", rather than trying to imperatively tell the timer to transition to a new state.
So, what you're trying to do is not really the "React" way, but you should still be able to make it work...
First, I'd recommend using a callback ref instead of a string ref, and ideally the callback is a class method on the outer component instead of a fat-arrow function so that a new function isn't generated with each render. Use the ref to capture the Timer instance on first render, store that instance as an instance variable of your Level1 component, and then use the instance later when the pause button is clicked.
Something like this maybe (untested of course):
class Level1 extends Component {
constructor(props) {
super(props);
this.captureTimer.bind(this)
this.onPauseTimerButtonClicked.bind(this)
this.state = {x: 0, y: 0};
}
captureTimer(ref) {
this.timer = ref
}
onPauseTimerButtonClicked() {
this.timer.pause()
}
render () {
...
return (
<div>
<div ref="elem" onMouseMove={this._onMouseMove.bind(this)} id="gameBoard">
<img id="waldo1" src={require('../images/waldo1(1).jpg')} alt="waldo"/>
<h2> {x} , {y}</h2>
</div>
<button onClick={this.onPauseTimerButtonClicked}>Pause</button>
<Timer ref={timer => this.captureTimer(timer)} options={OPTIONS}/> <-- Here Timer
</div>
) // return
} // render
} //component
The reason it's not working is probably because the pause function is not bound to the class.
Try binding it in the constructor of your component:
constructor(props) {
super(props)
this.state = { clock: 0, time: '' }
this.pause = this.pause.bind(this);
}
Try this pen: https://codepen.io/CarlosEME/pen/xgmYXZ
Assuming isWaldoFound is a method of Level1.. If so , replace :
Timer.pause()
By :
this.refs.mytimer.pause();