So i'm wondering if there is a difference between this:
import React, { Component, PropTypes } from 'react';
class Example extends Component {
constructor(props) {
super(props);
this.state = {
page : 1
};
}
nextPage = () => {
this.setState({ page: this.state.page + 1 });
}
previousPage= () => {
this.setState({ page: this.state.page - 1 });
}
render() {
const { page } = this.state;
return (
<div>
<H1>{page}</H1>
<Button onClickPrevious={this.previousPage} onClickNext={this.nextPage} />}
</div>
);
}
}
Or
import React, { Component, PropTypes } from 'react';
class Example extends Component {
constructor(props) {
super(props);
this.nextPage = this.nextPage.bind(this);
this.previousPage = this.previousPage.bind(this);
this.state = {
page: 1
};
}
nextPage() {
this.setState({ page: this.state.page + 1 }); }
previousPage() {
this.setState({ page: this.state.page - 1 }); }
render() {
const { page } = this.state;
return (
<div>
<H1>{page}</H1>
<Button onClickPrevious={this.previousPage} onClickNext={this.nextPage} />}
</div>
);
}
}
I'm wondering if it's the same in performance this to every function or are there any other benefits?
a bit of further reading(https://medium.com/#esamatti/react-js-pure-render-performance-anti-pattern-fb88c101332f#.khf30fuaq)
The best place to bind your event handlers is your constructor. This way your event handler has its context bound to the component instance.You can access props and state and call setState or forceUpdate from such bound handler.
Binding with arrow functions have their own advantages as well.
Arrow functions always gets the context from where they have been defined. So in fact, this example is equivalent to:
The arrow function syntax is a way of defining function with a syntax like this:
change = (ev) => this.setState({ text: ev.target.value });
It is a more concise way than writing a function(ev) { .... } statement. If you don’t provide { and } brackets after the => arrow, such function is a single expression which is returned instantly. So this desugars to something like:
change = function(ev) { return this.setState({ text: ev.target.value }); }.bind(this);
And hence both of .bind() and arrow function cause a new function to be created
Concluding, the way you want to bind your function depends on your use case.
For more details you can read up this article:
Related
I am using a setTimeout function which runs on a loop alternating between a boolean state using setState. However when this.setState gets called in the function I receive the following error:
TypeError: undefined is not an object (evaluating:
'this.state.percentages')
Below is a snippet of the code I am using - I would be very grateful to anyone who can point out the mistake I am making:
constructor(props) {
super(props);
this.state = {
percentages: false,
};
}
loopPercentages() {
setTimeout(function() {
this.setState({ percentages: !this.state.percentages });
loopPercentages();
}, 10000);
}
componentDidMount() {
this.loopPercentages();
}
import React from "react";
export class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
percentages: false
};
}
loopPercentages = () => {
setTimeout(() => {
this.setState({ percentages: !this.state.percentages });
this.loopPercentages();
}, 10000);
};
componentDidMount() {
this.loopPercentages();
}
render() {
return (
<div>
<h1>Hello StackOverflow</h1>
</div>
);
}
}
Use the setState callback function to get always the current state value:
loopPercentages() {
setTimeout(() => { //--> this component scope
this.setState((prev) => ({ percentages: !prev.percentages }));
this.loopPercentages();
}, 10000);
}
I want to call a function in render() which will update the state. But when I do that, it gives me this error message:
Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
I don't understand why this is happening because I'm not directly setting the state in my render(), I'm setting it in my setInsightUrl() function.
I've tried using different lifecycle functions but couldn't get any to work. I'm not sure how else to write this function.
import React, { Component } from 'react'
import "../../css/tabs/Tabs.css"
import {connect} from "react-redux"
class InsightPage extends Component {
constructor() {
super();
this.state = {
insightUrlState: null
}
this.setInsightUrl = this.setInsightUrl.bind(this);
}
setInsightUrl(url) {
this.setState({
insightUrlState: url
})
console.log(this.state.insightUrlState, 'INSIGHTTTTTT URLLLLLbgnhjm,k.l')
}
render() {
this.props.sideTreeMenu.dynamicMenu.forEach(obj => {
obj.children.forEach(child => {
child.insights.forEach(insight => {
if (insight.insightName === this.props.insightNameReducer) {
{this.setInsightUrl(insight.insightURL)}
}
})
})
})
return (
<div className={this.props.drawerOpen ? "tab_container2" : "tab_container" }>
<h1>Hello from Insight</h1>
<iframe frameBorder="0" style={{width: "100%", height: "70vh"}} src="https://insighttbdashboards.verizon.com/t/DigtalAnalytics/views/Digital_Analytics/Chat_Dashboard?iframeSizedToWindow=true&:embed=y&:showAppBanner=false&:display_count=no&:showVizHome=no#2" />
</div>
)
}
}
const mapStateToProps = state => ({
drawerOpen: state.SideDrawerReducer.open,
sideTreeMenu: state.SideDrawerReducer.menu,
insightNameReducer: state.SideDrawerReducer.insightName
})
export default connect(mapStateToProps)(InsightPage);
It should update the state with the url I am passing into the function in the render block.
Just because you are calling setState in a function defined outside of render (setInsightUrl) doesn't mean you aren't calling it within render, render potentially calls setInsightUrl when the right conditions are met, and thus can potentially loop forever.
Perhaps you could update the state only if it actually is changing:
setInsightUrl(url) {
if (this.state.insightUrlState != url) {
this.setState({
insightUrlState: url
})
console.log(this.state.insightUrlState, 'INSIGHTTTTTT URLLLLLbgnhjm,k.l')
}
}
From the code you posted (I'm not sure if that is the full code for your component) there's no need to determine the insight url in the render() function. If you do want to determine it in the render function (which should be the last thing your component does) then you shouldn't need to put it in the state, you should just use a local variable for it.
But if you want it in the state, you can either do it in the constructor:
constructor(props) {
super(props);
let insightUrlState = null;
props.sideTreeMenu.dynamicMenu.forEach(obj => {
obj.children.forEach(child => {
child.insights.forEach(insight => {
if (insight.insightName === props.insightNameReducer) {
insightUrlState = insight.insightURL;
}
});
});
});
this.state = { insightUrlState };
}
With an additional use of a lifecycle method if you want to update the state when the props change:
componentDidUpdate(prevProps, prevState) {
// depending on how many items are in these arrays, you might want to
// wrap this in a check to see if this.props.sideTreeMenu.dynamicMenu has
// changed from prevProps.sideTreeMenu.dynamicMenu and/or if
// this.props.insightNameReducer has changed from prevProps.insightNameReducer
let insightUrlState = null;
this.props.sideTreeMenu.dynamicMenu.forEach(obj => {
obj.children.forEach(child => {
child.insights.forEach(insight => {
if (insight.insightName === this.props.insightNameReducer) {
insightUrlState = insight.insightURL;
}
});
});
});
if (prevState.insightUrlState !== insightUrlState) {
this.setState({ insightUrlState });
}
}
Or, alternatively, you can use the getDerivedStateFromProps function to determine the insightUrlState value just before rendering (using this function, you don't need to use the constructor or componentDidUpdate options):
static getDerivedStateFromProps(props) {
let insightUrlState = null;
props.sideTreeMenu.dynamicMenu.forEach(obj => {
obj.children.forEach(child => {
child.insights.forEach(insight => {
if (insight.insightName === props.insightNameReducer) {
insightUrlState = insight.insightURL;
}
});
});
});
return { insightUrlState };
}
this.props.sideTreeMenu.dynamicMenu.forEach(obj => {
obj.children.forEach(child => {
child.insights.forEach(insight => {
if (insight.insightName === this.props.insightNameReducer) {
{this.setInsightUrl(insight.insightURL)}
}
})
})
})
This block is not valid JSX, you might need to move that to componentDidMount.
You can't call setState inside render, otherwise will cause a re-render, so it will go again to render and so on... That's why you got that error.
import React, { Component } from 'react';
import {withProvider} from './TProvider'
import ThreeCardMap from './ThreeCardMap';
class Threecard extends Component {
constructor() {
super();
this.state = {
newlist: []
}
}
componentDidMount(){
this.props.getList()
this.setState({newlist: [this.props.list]})
}
// componentDidUpdate() {
// console.log(this.state.newlist);
// }
render() {
const MappedTarot = (this.state.newlist.map((list, i) => <ThreeCardMap key={i} name={list.name} meaningup={list.meaning_up} meaningdown={list.meaning_rev}/>);
return (
<div>
<h1>Three Card Reading</h1>
<div>{ MappedTarot }</div>
</div>
)
}
}
export default withProvider(Threecard);
Hi, I'm trying to create a page that takes data from a tarot card API (https://rws-cards-api.herokuapp.com/api/v1/cards/search?type=major). Unfortunately by the time the data comes in, my map function has already fired. I'm asking to see if there is a way to have the map function wait until the data hits before it fires. Thanks!
Edit: getList function in the Context:
getList = () => {
console.log('fired')
axios.get('https://vschool-cors.herokuapp.com?url=https://rws-cards-api.herokuapp.com/api/v1/cards/search?type=major').then(response =>{
this.setState({
list: response.data
})
}).catch(error => {
console.log(error);
})
}
this.props.getList() is an async function. You are setting the list right after that call which is not correct.
You need to set it in the getList promise then() block.
getList() is an async function and update data for the parent component. So, my solution is just watching the list from the parent component if they updated or not, through getDerivedStateFromProps
class Threecard extends Component {
constructor() {
super();
this.state = {
newlist: []
}
}
// Set props.list to this.state.newList and watch the change to update
static getDerivedStateFromProps(nextProps, prevState) {
return {
newlist: nextProps.list
}
}
componentDidMount(){
this.props.getList()
// Removed this.setState() from here.
}
render() {
const MappedTarot = (this.state.newlist.map((list, i) => <ThreeCardMap key={i} name={list.name} meaningup={list.meaning_up} meaningdown={list.meaning_rev}/>);
return (
<div>
<h1>Three Card Reading</h1>
<div>{ MappedTarot }</div>
</div>
)
}
}
export default withProvider(Threecard);
This question already has answers here:
How can I call parent method in a child React component?
(2 answers)
Closed 4 years ago.
I have a child component which is a Redux form and from it's handleSubmit method, I need to call a method on the Parent Component. I try to do this by passing a callback as props from the Parent and nothing happens.
I've seen that this method works only when a function is called directly with an event handler on the child component.
import Parent from './parent.js';
class Child extends React.Component {
constructor(props) {
super(props);
};
callCloseModal = () => {
this.props.closeModal();
}
handleFormSubmit (values) {
this.callCloseModal()
}
render() {
<form onSubmit=
{handleSubmit(this.handleFormSubmit.bind(this))}>
.....
</form>
}
}
class Parent extends React.Component {
constructor (props) {
super(props)
this.state = {
modalOpen: false,
}
}
.....
handleModalClose() {
this.setState({ modalOpen: false })
}
render() {
<Child closeModal={this.handleModalClose}> {this.props.children}</Child>
}
}
How can I call a method on the parent component from a method on a child component?
Edit: The method was correct but it was one level higher (Grandparent component)
In your onSubmit handler:
render() {
<form onSubmit=
{handleSubmit(this.handleFormSubmit.bind(this))}>
.....
</form>
}
You call handleFormSubmit, but in its definition:
handleFormSubmit (values) {
this.callCloseModal
}
You only reference the callCloseModal. Notice callCloseModal is defined as an arrow function:
callCloseModal = () => {
this.props.closeModal();
}
So you need to call it. Try:
handleFormSubmit (values) {
this.callCloseModal();
}
I guess this would work as expected. Just call callCloseModal as a function inside handleFormSubmit
class Child extends React.Component {
constructor(props) {
super(props);
};
callCloseModal = () => {
// ideally you want to check if props is a func
if (typeof this.props.closeModal === 'function') {
this.props.closeModal();
}
}
handleFormSubmit = (values) => { // no need to mix binding with arrow funcs
this.callCloseModal() // should be calling as a function
}
render() {
<form onSubmit=
{handleSubmit(this.handleFormSubmit)}>
.....
</form>
}
}
you just have to change the function from this
callCloseModal = () => {
this.props.closeModal();
}
to this
callCloseModal = () => {
this.props.handleModalClose();
}
Let me know if you face any other issue.
I'm trying to abstract away my onClick function(s) into its own file and then later use it in a component's button(s).
However the function needs to conditionally update the state of the component it is being used in.
export let ButtonClickHandlers = {
refreshDatabase: (url: string) => {
service.fetchJson<any>(url)
.then(res => {
if(res.isSuccessStatusCode){
//update state
}
else{
//update state
}
});
},
//more functions to be added later
}
In my component I want to do something like this:
import {ButtonClickHandlers} from '../ButtonClickHandlers';
<button onClick = {ButtonClickHandlers.refreshDatabase} />
How do I implement this functionality?
This is how i have done it, there might be better ways.I have imported the clickevent and passed the context as paremeter
clickevenhandler.js
const clickEvent = (context) => {
context.setState({ click: true });
}
export default clickEvent;
my test component
import clickEvent from './clickhandler';
class TestComp extends React.Component<any, any>{
constructor(props) {
super(props);
this.state = {
click: false
}
}
renderclicked() {
if (this.state.click) {
return (<div>clicked</div>);
}
else {
return (<div></div>);
}
}
render() {
return (<div>Test
<button onClick={() => clickEvent(this)}>click</button>
{this.renderclicked()}
</div>);
}
}