Running setState prop function hang Maximum call stack - reactjs

On my template js I have the following
updateBarTitle(title){
this.setState({barTItle:title});
}
render() {
const childrenWithProps = React.Children.map(this.props.children, function (child) {
var childProps = {
updateBarTitle: this.updateBarTitle.bind(this)
};
var childWithProps = React.cloneElement(child, childProps);
return childWithProps;
}, this);
And on the child I have.
componentDidUpdate(){
this.props.updateBarTitle('Users');
}
When testing out the app and once state changes my browser freezes for a long time then returns a Maximum call stack size exceeded Need some advice on what I'm doing wrong.

You're creating an infinite loop with the child componentDidUpdate() method. It calls updateBarTitle(), which calls setState(), which re-renders, which calls updateBarTitle() again...and so on.
If you want that logic in the componentDidUpdate() method where you have it, you need to add some condition so that it will only call once.
You could pass the current state of barTitle to the child as a prop and then do something like:
if(this.props.barTitle !== 'Users') {
this.props.updateBarTitle('Users');
}
To stop that loop from happening for example.
EDIT: To give a clearer example here is a DEMO of this to illustrate.

Related

set state in a callback of an async function

I am new to React, so bear with me please. I have a component that calls another component that takes a property. This property will get it's value on a callback of a function, something like this:
render(){
myFunc((p) => {
if(!_.isEqual(p, this.state.myProp))
this.setState({myProp: p})
});
return <MyComponent myProp={this.state.myProp}/>
}
myFunc will or will not make an API request and depending on that will call the callback sooner or later. This seems to work fine when API request is made and the callback takes longer to return. However, when the request is not needed and callback returns instantaneously (or almost) I am getting a Warning: Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state.
What am I doing wrong and what is the right way to approach this? Where would be the right place to put this code? Basically what I need is to re-render MyComponenent if this.state.myProp changes
You shouldn't be calling setState inside the render method, you might end up having an infinite loop.
The call to myFunc should be somewhere else (depending on the business logic you have). When the function finishes, it will update the state and then trigger a re-render so MyComponent will get the latest value.
UPDATE
I don't know which conditions will require calling myFunc again, but you can do:
state = {
myProp: null // or some other value that MyComponent can handle as a null state
}
componentDidMount () {
myFunc((p) => {
if(!_.isEqual(p, this.state.myProp)) // This is needed only if you get null back from the callback and you don't want to perform an unnecesary state update
this.setState({myProp: p})
}
}
render(){
const { myProp } = this.state
// You can also do if (!myProp) return null
return <MyComponent myProp={myProp}/>
}

Component not updating on stateChange

I would like the value of a TextField to be some computed value... as such, I have a controlled form, ComputedValue, with a render method that looks like this:
render() {
return <TextField
value={this.state.value}
key={this.props.id}
id={this.props.id}
label={this.props.label}
inputProps={{
readOnly: true
}}
/>;
}
the ComputedValue's state.value is set by pulling data from localStorage, doing some computation, and setting the state. That method looks like this:
computeValue() {
let computedValue="";
// split the computation string into constituent components
let splitCS = this.props.computationString.split(/([+,-,*,/,(,),^])/g);
// replace all instaces of questionID's with their value
for (let i = 0; i < splitCS.length; i++) {
if (splitCS[i] !== '+' && splitCS[i] !== '-' && splitCS[i] !== '*' && splitCS[i] !== '/' &&
splitCS[i] !== '(' && splitCS[i] !== ')' && splitCS[i] !=='^') {
// splitCS[i] is a questionID
let Q = getQuestionDataFromLSbyQuestionID(splitCS[i]);
splitCS[i] = Q.value;
}
}
// check that all values returned
if(splitCS.includes("")) {
console.log("null value was returned in ComputedValue question");
} else {
// rejoin string and send to eval (TODO: replace eval)
let finalComputeString = splitCS.join('')
computedValue = eval(finalComputeString);
}
// save value in state
this.setState({
value: computedValue
}, () => {
this.props.stateChangeHandler(this);
this.render();
}
);
return computedValue;
}
This component is leaf in a tree of questions; as their values change (my app syncs LS and the head parent's state) I'd like the computed value to change.
Here are my two issues:
1) Given I call setState inside the computeValue function... I'm unable to call it from within the render function. However, if I only call it from the componentWillMount lifecycle function, it computeValue only gets called once (upon mounting) and then not again. If I call it from the componentWillUpdate, I get an infinite loop. I can put a stop in this loop by comparing nextStates and what not... but that doesn't seem like the best way.
2) renders are not triggered by an update of the parent's state.. including trying a 'forceUpdate') (though they do appear to be triggered by hovering over the mouse and various other UI things).
Given I call setState inside the computeValue function... I'm unable
to call it from within the render function.
I would like you to elaborate or clarify this statment.
However, if I only call it from the componentWillMount lifecycle
function, it computeValue only gets called once (upon mounting) and
then not again.
Yes componentWillMount is a lifecycle hook that is called only once when the component is going to be added in the tree.
If I call it from the componentWillUpdate, I get an infinite loop.
This lifecycle hook is called before your component gets updated. So if you set your state here it will again call this function and it will make a infinite loop.
I can put a stop in this loop by comparing nextStates and what not...
but that doesn't seem like the best way.
You can always do the comparing and update the state. There is nothing like a wrong way in doing so.
renders are not triggered by an update of the parent's state..
including trying a 'forceUpdate')
You should never call render explicitly. It is not a correct practice because it hinders normal flow the react.
I would like you to test the setstate
this.setState(() => ({
value: computedValue
}), () => {
console.log("working")
this.props.stateChangeHandler(this);
}
);
The problem seems to be the () in ({ value: computedValue }). Give a try.
I think your answer is in your second observation. [renders are not triggered by an update of the parent's state]. guess what triggers render from the Parent? props
so, instead of putting your value in the state, put it in the prop, that way if the parent changes it, your component changes.
Hope this helps

Stop React from rendering code that opens websockets

I have this code in render() that opens a websocket and connects to a Rest service.
return (
<div className="App">
<SockJsClient
url = 'http://localhost:8610/refresh/'
topics={['/topic/notification']}
onConnect={console.log("Connection established!")}
onDisconnect={console.log("Disconnected!")}
onMessage={(msg) => this.update()}
debug= {true}
/>
Everything works fine - the only issue with this approach is, that when the UI is rerendered, the application closes the websocket and reopens it again. Nothing breaks, the app just resumes and nothing happens.
Is this operation very resource consuming? Is there any way to tell react not to re-render that portion of code? Should I be worrying at all?
this.update() re-sets an array in my state.
I know shouldComponentUpdate() is what I need, but then I would have to create a new component that does the rerendering, right? I'm confused.
Thanks!
EDIT: This also works (#zavjs's solution is much more cleaner)
componentDidMount(){
var self = this; // use self to reference this
var socket = new SockJS("http://192.168.1.139:8610/refresh");
var stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame,) {
console.log('connected: ' + frame);
stompClient.subscribe('/topic/notification', function(notification) {
self.update(); // here
})
}, function(err) {
console.log('err', err);
});
Also here's more about this!
Yes, I'd create an intermediary component, to wrap around your SocketJSClient, and condition shouldComponentUpdate to whenever you purposefully want to make that re-render happen (perhaps when a Socket config like url has changed and you need to pass that down, for instance).
class PreventUpdate extends React.Component {
shouldComponentUpdate = (nextProps) => {
//set true if a Socket connection config has changed
//or something that needs to trigger a re-render in the
//child component
if(this.nextProps.shouldPreventUpdate) {
return false;
}
};
render() {
this.props.children;
}
}
And now you can do:
<PreventUpdate
shouldPreventUpdate={someDynamicCondition}>
<SocketJSClient />
<PreventUpdate/>
With this you can pass in an arbitrary flag to prevent update of a component and all its children. I hope it fits you well

Throttling dispatch in redux producing strange behaviour

I have this class:
export default class Search extends Component {
throttle(fn, threshhold, scope) {
var last,
deferTimer;
return function () {
var context = scope || this;
var now = +new Date,
args = arguments;
if (last && now < last + threshhold) {
// hold on to it
clearTimeout(deferTimer);
deferTimer = setTimeout(function () {
last = now;
fn.apply(context, args);
}, threshhold);
} else {
last = now;
fn.apply(context, args);
}
}
}
render() {
return (
<div>
<input type='text' ref='input' onChange={this.throttle(this.handleSearch,3000,this)} />
</div>
)
}
handleSearch(e) {
let text = this.refs.input.value;
this.someFunc();
//this.props.onSearch(text)
}
someFunc() {
console.log('hi')
}
}
All this code does it log out hi every 3 seconds - the throttle call wrapping the handleSearch method takes care of this
As soon as I uncomment this line:
this.props.onSearch(text)
the throttle methods stops having an effect and the console just logs out hi every time the key is hit without a pause and also the oSearch function is invoked.
This onSearch method is a prop method passed down from the main app:
<Search onSearch={ text => dispatch(search(text)) } />
the redux dispatch fires off a redux search action which looks like so:
export function searchPerformed(search) {
return {
type: SEARCH_PERFORMED
}
}
I have no idea why this is happening - I'm guessing it's something to do with redux because the issue occurs when handleSearch is calling onSearch, which in turn fires a redux dispatch in the parent component.
The problem is that the first time it executes, it goes to the else, which calls the dispatch function. The reducer probably immediately update some state, and causes a rerender; the re-render causes the input to be created again, with a new 'throttle closure' which again has null 'last' and 'deferTimer' -> going to the else every single time, hence updating immediately.
As Mike noted, just not updating the component can you get the right behavior, if the component doesn't need updating.
In my case, I had a component that needed to poll a server for updates every couple of seconds, until some state-derived prop changed value (e.g. 'pending' vs 'complete').
Every time the new data came in, the component re-rendered, and called the action creator again, and throttling the action creator didn't work.
I was able to solve simply by handing the relevant action creator to setInterval on component mount. Yes, it's a side effect happening on render, but it's easy to reason about, and the actual state changes still go through the dispatcher.
If you want to keep it pure, or your use case is more complicated, check out https://github.com/pirosikick/redux-throttle-actions.
Thanks to luanped who helped me realise the issue here. With that understood I was able to find a simple solution. The search component does not need to update as the input is an uncontrolled component. To stop the cyclical issue I was having I've used shouldComponentUpdate to prevent it from ever re-rendering:
constructor() {
super();
this.handleSearch = _.throttle(this.handleSearch,1000);
}
shouldComponentUpdate() {
return false;
}
I also moved the throttle in to the constructor so there can only ever be once instance of the throttle.
I think this is a good solution, however I am only just starting to learn react so if anyone can point out a problem with this approach it would be welcomed.

Change state when properties change and first mount on React - Missing function?

I have come across a problem about states based on properties.
The scenario
I have a Component parent which creates passes a property to a child component.
The Child component reacts according to the property received.
In React the "only" proper way to change the state of a component is using the functions componentWillMount or componentDidMount and componentWillReceiveProps as far as I've seen (among others, but let's focus on these ones, because getInitialState is just executed once).
My problem/Question
If I receive a new property from the parent and I want to change the state, only the function componentWillReceiveProps will be executed and will allowed me to execute setState. Render does not allow to setStatus.
What if I want to set the state on the beginning and the time it receives a new property?
So I have to set it on getInitialState or componentWillMount/componentDidMount. Then you have to change the state depending on the properties using componentWillReceiveProps.
This is a problem when your state highly depends from your properties, which is almost always. Which can become silly because you have to repeat the states you want to update according to the new property.
My solution
I have created a new method that it's called on componentWillMount and on componentWillReceiveProps. I have not found any method been called after a property has been updated before render and also the first time the Component is mounted. Then there would not be a need to do this silly workaround.
Anyway, here the question: is not there any better option to update the state when a new property is received or changed?
/*...*/
/**
* To be called before mounted and before updating props
* #param props
*/
prepareComponentState: function (props) {
var usedProps = props || this.props;
//set data on state/template
var currentResponses = this.state.candidatesResponses.filter(function (elem) {
return elem.questionId === usedProps.currentQuestion.id;
});
this.setState({
currentResponses: currentResponses,
activeAnswer: null
});
},
componentWillMount: function () {
this.prepareComponentState();
},
componentWillReceiveProps: function (nextProps) {
this.prepareComponentState(nextProps);
},
/*...*/
I feel a bit stupid, I guess I'm loosing something...
I guess there is another solution to solve this.
And yeah, I already know about this:
https://facebook.github.io/react/tips/props-in-getInitialState-as-anti-pattern.html
I've found that this pattern is usually not very necessary. In the general case (not always), I've found that setting state based on changed properties is a bit of an anti-pattern; instead, simply derive the necessary local state at render time.
render: function() {
var currentResponses = this.state.candidatesResponses.filter(function (elem) {
return elem.questionId === this.props.currentQuestion.id;
});
return ...; // use currentResponses instead of this.state.currentResponses
}
However, in some cases, it can make sense to cache this data (e.g. maybe calculating it is prohibitively expensive), or you just need to know when the props are set/changed for some other reason. In that case, I would use basically the pattern you've written in your question.
If you really don't like typing it out, you could formalize this new method as a mixin. For example:
var PropsSetOrChangeMixin = {
componentWillMount: function() {
this.onPropsSetOrChange(this.props);
},
componentWillReceiveProps: function(nextProps) {
this.onPropsSetOrChange(nextProps);
}
};
React.createClass({
mixins: [PropsSetOrChangeMixin],
onPropsSetOrChange: function(props) {
var currentResponses = this.state.candidatesResponses.filter(function (elem) {
return elem.questionId === props.currentQuestion.id;
});
this.setState({
currentResponses: currentResponses,
activeAnswer: null
});
},
// ...
});
Of course, if you're using class-based React components, you'd need to find some alternative solution (e.g. inheritance, or custom JS mixins) since they don't get React-style mixins right now.
(For what it's worth, I think the code is much clearer using the explicit methods; I'd probably write it like this:)
componentWillMount: function () {
this.prepareComponentState(this.props);
},
componentWillReceiveProps: function (nextProps) {
this.prepareComponentState(nextProps);
},
prepareComponentState: function (props) {
//set data on state/template
var currentResponses = this.state.candidatesResponses.filter(function (elem) {
return elem.questionId === props.currentQuestion.id;
});
this.setState({
currentResponses: currentResponses,
activeAnswer: null
});
},

Resources