Correctly using React this.state and truly being a state machine

We are all trained in React that NEVER EVER do this.setState() (or derivative) in your render() method, right? It is considered evil.  There is general fantasy that render() should be 100% benign, no side effects, 100% repeatable no matter how often re-called, right?

There are two main reasons for this fantasy. One is some purist view that render should not be smart or think and rather just reflect some reality.  The other is that setState() is just evil; unpredictable, and WILL cause your render to get called from within itself (endless crashing recursion) if you’re not careful.

First, let’s dismiss the purist view.  We’ve all seen (and code ourselves) renders that return an entirely different set of objects based on some set of conditions; often, your render method might just return <div>Busy fetching</div> right? (or a busy wheel).  We see this all the time. Or we return a different set of components based on who the current user is or privileges, and so forth.  There is nothing wrong with this style.

The bigger issue I run into, and often, is that my component realizes during its render attempt that it needs to do more work, namely fetch more data or images from the server.  For example, a mouse-click in *another* component results in a list-item-selection and now your component needs to do some verification or recording etc and you don’t learn of this until re-render.  In complicated many-panel component systems this occurs frequently.

The temptation is to detect this and then record (in state) that you’re off waiting for results and respect that if re-rendered again before finished.  But you can’t.  You’ll get React warnings and possibly have to directly alter your ‘state’ (in addition to calling setState()) so that your own decision tree can use the new status or endless recursion.

Here’s the real problem:  Most of us are React-trained (even if we have decades of javascript experience) into believing there is magic…that your React components are not normal animals in the zoo…they are something very special. But they are not. They are just classes (or functions), yet we think that the only place to store component (class) related data is “in state”.  It’s every example you see.  It is how and why the seeming-magic of cascading updates takes place.

But you can have as much per-instance class data you wish, call it what you want, store it where you want, and directly update it as you see fit.  For example, consider this:

render() {

if (this.busy || this.props.currentUser !== this.lastKnownUser) {

this.busy++;

this.fetchNewUserData();

return (<div>Busy…</div>)

}}

Of course the fetch() code will clear the busy flag when ready and possibly call this.setState() to cause a new render invocation.  In fact, all of my components heavily perform server-I/O and use a common class invocation called “comm()” (unique per component instance) and comm sports a “comm.isBusy()” method for easy checking by the parent.  In other words, if my component is doing I/O, consider it busy, exit the render() early, and presume that when the I/O is complete it will re-render and with appropriate data. 

So let’s feel free to use our components like regular javascript classes and store local data and ‘state machine’ type of information as-needed. The only data that really should be in state or props is data that IF UPDATED should cause re-rendering of any component using that data (like a displayed username) and not bookkeeping data (like current user privs).

Leave a Comment

Your email address will not be published. Required fields are marked *