How can i modify state from two different function call? Following code gives me error'Maximum update depth exceeded.'
class App extends Component {
// fires before component is mounted
constructor(props) {
// makes this refer to this component
super(props);
// set local state
this.state = {
date: new Date(),
myQuestions:myQuestion,
counter :0,
activeQuestion:-1,
};
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
this.handleClick = this.begin.bind(this)
}
begin(){
this.setState({activeQuestion:1})
}
handleClick() {
if(this.state.activeQuestion <= myQuestion.length){
this.setState(prevState => ({
counter: this.state.counter + 1,
activeQuestion:this.state.activeQuestion+1
}));
}
render() {
return (
<div className="App">
<div id = "myQuiz">
<div class ="intro {{ (activeQuestion > -1)? 'inactive':'active' }}">
<h2>Welcome</h2>
<p>Click begin to test your knowledge</p>
<p class = "btn" onClick={this.begin('begin')}>Begin</p>
</div>
<div>
<button onClick={this.handleClick}></button>
</div>
What will be the right way to change state from different function call?
You are not passing function to cllick handler. Instead you are calling the function like
onClick={this.begin('begin')}
This line is causing an infinite loop because calling this function is updating the state, which in turn is calling the render function. Change this to
onClick={this.begin}
If you want to pass the parameter to handler then
onClick={() => this.begin('begin')}
You have multiple issues in your code.
You override this.handleClick with the bound version of begin() in your constructor and this.begin will still not be bound to this.
Where does myQuestion come from in the constructor? Is it a globally scoped variable?
In your handleClick() you do not use the prevState to calculate the next state. Instead you use this.state which may lead to bad behavior if react batches multiple calls to setState().
Why do you use an onClick handler on a paragraph? Shouldn't this be a button?
You have to use className instead of class in jsx because class is a reserved keyword in javascript.
Also the class you are trying to set will be literally the text intro {{ (activeQuestion > -1)? 'inactive':'active' }}. This is for sure not what you where trying to achieve.
Related
If I receive a prop and change it in my functional component, can it create a problem? Or it's ok to change it inside the component?
e.g
const MyComponent = ({ foo }) => {
// is this bad?
foo = someCondition ? bar : foo
return (...)
}
I know that I could create another variable and store the values, but I would like to know if changing the prop it self won't cause any problem because it's a functional component.
No, it shouldn't create any problems. As with regular functions the arguments passed are their own variables in the function scope and don't mutate the original value passed to the function.
function something(value) {
value = 'nothing';
}
var anything = 0;
something(anything);
// Anything should still be 0;
console.log(anything);
But I would suggest to not mutate your variables.
If foo in your example is passed from the parrent, and the parrent keeps it in its state, then you would also need to pass setFoo as a paramater to your component and use that to update it properly.
function Parrent(){
let [foo, setFoo] = useState('foo');
return <Child foo={foo} setFoo={setFoo}/>
}
As for changing the props directly, you can if they are arrays or objects.
Props in the React are just read-only variables. You should change the props values by the parent component
I avoid changing the prop.
But I created a simple example and changing the prop in the children do not affected the value in the parent component.
https://codesandbox.io/s/objective-cdn-cq55d
I tested it with several render. Thats why I added an input. Typing in it makes the component rerender.
const MyComponent = ({ foo }) => {
// Not valid
foo = someCondition ? bar : foo
return (...)
}
There are two kinds of data in React,
a)Props(immutable data)
b)State(mutable data)
you are not supposed to change the immutable data(there are some ways to do it, but not recommended). what you should do is, (you can't assign a callback and change from here, i'll explain later why)
if you want to just use just the value inside this component
const baz = foo === condition ? bar : foo
or render something based on foo meets some condition
return (
<div>
{foo === somecondition ? <A /> : <B />}
</div>
)
Or you want to actually change it,
coming from a global state like redux or mobx,
u should change it from the reducers in case of redux or
#action decorated functions in mobx.
if it's a local state which passed down to the child component,
u can set a call back and assign to a click handler in which the case it is feasible
handleClick = () => {
this.setState(prevState => ({
...prevState,
foo: someCondition ? bar : foo,
}))
}
render () {
const { handleClick } = this
return <ChildComponent {...{ handleClick }} />
}
Like said before u can't change the passed down local state from render of the child(or render of any component[actually u can, but may end up in infinite loops: each time a state change happens, the component will re render, so the loop(pure components excluded eg: shouldComponentUpdate() hook which validates an unrelated condition)])
in such cases what u should do is to make the child component also a stateful component and change the parent props with a callback
class Child extends Component {
//set once
componentWillMount() {
if (condition) callback
}
//whenever there is change
componentWillReceiveProps(nextProps) {
if (condition) callback
}
//use correct lifecycle method which meets your requirement..
}
My question is to do with the issue React has for binding functions in the render function.
The following is not good practice:
render() {
<div onClick={this.callFunction.bind(this)}/>
}
as each re render would add a new function to the page, eventually causing the browser to run out of memory.
The solution is to do this:
constructor() {
this.callFunction = this.callFunction.bind(this);
}
render() {
<div onClick={this.callFunction}/>
}
The problem with this is when I want to pass a value into the function.
I know I can make the div a child component, and pass the parameter in through the callBack, but this does not seem sensible if the div is only being used once in the whole application. I accept I could make this work, but this is not the scope of this question.
I also know that this solution:
render() {
<div onClick={() => this.callFunction.call(this, param)}/>
}
Is no better, as it is still creating a new function.
So my question is, how can I create a function that I can pass a parameter into without making a new component, and without binding a new funciton on each render?
You can't avoid creating a second component as you need to pass a function reference as an event handler, this will be executed by the browser when the event triggers.
So the problem is not the binding but the fact that you need to pass a reference, and references can't receive parameters.
EDIT
By the way, if you don't like the syntax and noise of binding or anonymous arrow functions you can use currying.
I posted an example in a different question if you find it interesting. this won't solve the problem though, it's just another approach to pass a new reference (which i find it to be the most terse)
You can change the declaration of callFunction to be an arrow function, which implictly binds the scope, like so:
callFunction = () => {
console.log('hi');
};
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
Then your original render function would work as expected!
Use Side effect
Side effect is something that a function use that comes from outside but not as argument. Now this mechanism is majorly used in Redux/Flux where the entire state is stored in a Store and every component fetches their state from it.
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
handlerProps: {
onClick: { count: 0},
onChange: { count: 0}
}
}
}
onClickHandler = () => {
const state = this.state.handlerProps.onClick;
console.log('onClick', state.count);
}
onChangeHandler = (value) => {
const state = this.state.handlerProps.onChange;
console.log('onClick', state.count);
this.setState({value: value})
}
buttonClick = () => {
const random = Math.ceil(Math.random()* 10) % 2;
const handler = ['onClick', 'onChange'][random];
const state = this.state.handlerProps;
state[handler].count++;
console.log('Changing for event: ', handler);
this.setState({handlerProps: state});
}
render () {
return (
<div>
<input onClick={this.onClickHandler} onChange={this.onChangeHandler} />
<button onClick={ this.buttonClick }>Update Props</button>
</div>
)
}
}
ReactDOM.render(<MyComponent/>, document.querySelector('.content'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react-dom.min.js"></script>
<div class='content' />
The only way I know of is to create a new React Component which takes the value and the event handler as props.
This way, the handler as a function remains static, and since the value is passed down separately (in its own prop) you don't have any functions being re-instanciated. Because you don't bind anything nor create a new function each time.
Here's an example:
We have two buttons. The first one prints the current state variable value and the other increments it by one.
Normally, if we had done this with onClick={() => this.print(this.state.value)} we would get a new instance of this function, each time the MyApp component would re-render. In this case, it would re-render each time we increment the value with the setState() inside this.increment.
However, in this example, no new instance of this.print happens because we are only passing its reference to the button. In other words, no fat arrow and no binding.
In the <Button /> component, we have a <button> to which event handler we pass a reference to a function - just like we did in <MyApp />. However, here we know exactly what to pass to the function. As such, we have myHandler trigger this.props.handler(this.props.value).
class MyApp extends React.Component {
constructor() {
super();
this.state = {
value: 0
};
}
print = (value) => {
console.log(value);
}
increment = () => {
// This will trigger a re-render, but none of the functions will be reinstanciated!
this.setState((prevState) => ({value: prevState.value + 1}));
}
render() {
// Note that both handlers below are passed just the references to functions. No "bind" and no fat arrow.
return(
<div>
<Button handler={this.print} value={this.state.value}>Print</Button>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
class Button extends React.Component {
// Clicking the button will trigger this function, which in turn triggers the function with the known argument - both of which were passed down via props.
myHandler = () => this.props.handler(this.props.value);
render() {
// Note again that the handler below is just given the reference to a function. Again, not "bind" nor fat arrow.
return(
<button onClick={this.myHandler}>{this.props.children}</button>
);
}
}
ReactDOM.render(<MyApp />, 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>
Though quite tedious, it is an effective solution. That being said, even if you do create a new function each time you render, the performance implications are minimal. From the official docs:
The problem with this syntax is that a different callback is created each time the LoggingButton renders. In most cases, this is fine.
As you might know we can create arrow functions as event handlers without binding this (with Babel Stage 2 support). In React with Redux we have Smart and Dumb Components. Hence I would like to create state modifying event handlers in the Smart Components with default params (for reusability) and pass them on to Dumb Components as props as detailed below : -
// SmartComponent.js
class SmartComponent extends Component {
state = {count: 0};
handleClick = (inc = 1) => this.setState({count: this.state.count + inc});
render = () => <DumbComponent handleClick={this.handleClick} />
}
// DumbComponent.js
function DumbComponent(props) {
return <button onClick={props.handleClick}>Submit</button>;
}
The problem is I get a console log stating that the passed arrow function is an object and not a function. It would work as expected if we create an arrow function as below, or if the initial arrow function doesn't have a default param in the DumbComponent.
// DumbComponent.js
function DumbComponent(props) {
return <button onClick={() => this.props.handleClick()}>Submit</button>;
}
Any ideas/suggestions to make it work without a second arrow function would be much appreciated.
I have a parent component, <App>:
constructor() {
super();
this.state = {
transporterPos: 0
}
this.tick = this.tick.bind(this);
}
componentDidMount() {
this.timerId = setInterval(() => this.tick(), 1000);
}
componentWillUnmount() {
clearInterval(this.timerId);
}
tick() {
let transporterPos = this.state.transporterPos;
transporterPos++;
if (transporterPos > 7) {
transporterPos = 0;
}
this.setState({ transporterPos: transporterPos });
}
render() {
return (
<div>
<Staves transporterPos={this.state.transporterPos}/>
</div>
);
}
The <Staves> component contains several <Stave> components, each of which contains several <Note> components. Each <Note> component is injected with a className conditional on its active property being true:
<div className="noteContainer" onClick={this.handleClick}>
<div className={"note" + (this.props.active ? ' active' : '')}></div>
</div>
handleClick() is a method that toggles a <Note>'s active property. I'm not including all the code here to make this more readable. The problem is that when clicking on a <Note>, although its active property changes immediately, the styling given by the conditional className of 'active' is not visible until the component is re-rendered at the next "tick" of the setInterval method. In other words, rendering only seems to happen once every 1000ms. I would like it to happen immediately. Am I using setInterval wrong?
Edit:
In response to comments, here is the handleClick method (in <Note>):
handleClick() {
this.props.toggleActive(this.props.pos);
}
This calls toggleActive in <Stave>:
toggleActive(pos) {
this.props.notes[pos].active = !this.props.notes[pos].active;
}
props.notes here is part of <App>'s state, which is passed down to <Stave> (and which I didn't include in this question for the sake of brevity).
toggleActive(pos) {
this.props.notes[pos].active = !this.props.notes[pos].active;
}
The reason a re-render isn't being triggered is because this.props is mutated directly instead of with setState. Move toggleActive further up to where you can use setState.
If necessary you can pass the function as a prop to the child component and call it via this.props.toggleActive()
Besides not triggering a re-render, another reason this.props should never be mutated directly is because your changes will get overwritten whenever the parent changes state and passes props to its children.
I am new to React and js I need some clarifications on binding of methods in components
I have 2 components ParentComponent and ChildComponent
Parent
var ParentComponent = React.createClass({
methodArg: function (value) {
console.log("methodArg called", value);
},
methodNoArg: function () {
console.log("methodNoArg called");
},
render: function () {
return <ChildComponent m1={(value) => this.methodArg(value)} m2={() => this.methodNoArg} />
}
})
Child
var ChildComponent = React.createClass({
render: function () {
return (
<div>
<button onClick={()=>this.props.m1(100)}>Call M1</button>
<button onClick={()=>this.props.m2()}>Call M2</button>
</div>
)
}
})
When i click Call M1 button, methodArg() of parent is getting called.
But when I click Call M2 methodNoArg() is not getting called. What is the issue in this ?
When I pass methodNoArg to Child , it is getting called
<ChildComponent m1={this.methodArg()} m2={this.methodNoArg} />
But methodArg() is getting called without clicking the button , its getting called everytime when the child component is rendered.
<button onClick={()=>this.props.m1(100)}>Call M1</button>
Your above line is saying evaluate the m1 method and assign the result to onClick. So when you refresh your page you console.log statement with value you passed gets printed but it will never gets called again no matter how many times you click the button as onClick now does not have method assigned to it now.
You can achieve what you want by removing that parenthesis which is calling the method automatically without clicking.
Here is working jsfiddle code link :
http://jsfiddle.net/fp0LvkLg/
This is because of the way you assign your method to a prop
m2={() => this.methodNoArg}
is (kind of if we omit the intricacies of this) equivalent to
m2={function() {return this.methodNoArg}}
So your prop is a function, which returns a function, which in turn is never called.
You want to simply assign your function to a prop like this:
m2={this.methodNoArg}