You’re the popular figure on the Internet now and frankly, I felt rather unsure when I was first introduced to you. Your style seemed quirky, especially how you seemingly went against the holy grail of engineering advice, encouraging us to break the principle of maintaining a separation of concerns.
Strangely, our initial interactions went well, in fact, a little too perfect I must say. It was easy adapting to this unconventional style you call JSX, and I started to see the additional values you provided through multiple lifecycle hooks.
But alas, as our relationship grew deeper, I fell into my comfort zone and ended up dropping the ball on several occasions. I drew upon my experience with others and tried applying the same principles to you, and that was a foolish decision since everyone operates in a different manner.
Even though I managed to salvage the situation and repaired the damage over time, I couldn’t help but feel that all these could have been avoided altogether if I had let go of my inaccurate expectations developed through previous experiences.
Thus, if I were to start over with you again, here are some of the important pitfalls I would avoid repeating.
Stores are not Models
React.js integrates well into this design paradigm known as Flux and within it, there exists a concept known as Stores — the source of truth for our application’s logic.
Stores bear a close resemblance to Models, but they shouldn’t be designed in the same way.
Models are collections of objects, each representing a single record of data that may be shared across multiple domains of logic. Stores, on the other hand, represent the application state for one particular domain within our application.
I made the mistake of designing Stores as if they’re Models, staying close to my database schema, and haphazardly applied my experience with the latter within a Flux application.
Things may have worked, to a certain degree, but not without a series of unnecessary frustrations and struggles which would have been avoided if I hadn’t tried to force it to adapt to a familiar concept from a different design paradigm.
Bottom line is, don’t be afraid to deviate from your database schema even if you end up with duplicated subsets of data across different stores. It’s still better than having multiple domains share data objects which may be inadvertently modified, causing unintended state changes to unrelated components.
And after all, Stores are not Models.
Don’t be Trigger-happy
React has gained a reputation for being really fast in responding to changes, and that it’s performant right out the box but still, it should not be abused.
There I said it — I was lazy and adopted a “fire and forget” mentality, emitting change events whenever Actions are dispatched without putting in much thought as to whether UI updates are required, letting the library do the hard work in figuring things out.
This was simply irresponsible and sad to say, somewhat amateur. Even though it’s fast and responsive, the logic that needs to be evaluated before the eventual `setState` method call may be expensive, leading to unnecessary bottlenecks in our application.
Besides, if there is a large number of components nested under the tree(s) listening for these change events, unnecessary render calls will have a negative impact on the performance of our application.
Code defensively, a library or framework’s features are there to aid you, not do all the hard work for you.
Keep It Simple, Stupid
As a software engineer with a healthy ego, it always feels good to come up with complicated but cool looking diagrams and / or flow charts that represent an application’s architecture.
That one diagram provided to explain Flux looked really simple, and I found it hard to believe that it can be applied to complex applications.
I proceeded to modify that diagram, keeping the unidirectional data flow, but adding more components such as a Mediator, a Services layer to handle network requests, toyed with having a 2nd Dispatcher and topped it off with awesome looking arrows connecting them all together.
The finished product looked really good on paper. I definitely felt proud of my drawing but when it was time to implement a feature, I was overwhelmed. My mental models were completely screwed thanks to the complexity of whatever I came up with.
Eventually, I discarded my awesome looking architecture (still kept the drawing, just tossed away the thought) and reverted back to that simple looking diagram you provided me with.
I should have trusted the diagram from the start. It is good enough. ☺
Don’t mess with State
Relationships are built on trust — so make sure that whenever we pass data to a component as a representation of it’s state, that we do not randomly decide to manipulate its value within the lifecycle hooks that React provides us.
Manipulation of state should be restricted to the Stores and if there’s a need to enqueue atomic updates based on previous values, it can be done within the callback of `this.setState`, and never directly on `this.state`. The latter should only be read from and never manipulated on.
For a moment, I toyed with manipulating state within `shouldComponentUpdate` as it was the only lifecycle hook where I have access to both previous and current states. Needless to say, it let to awkward hours spent debugging strange application behaviours.
Maintain a discipline to only read from state and to never manipulate state objects, this will save us a lot of headaches when we meet unexpected behaviours in our application.
We’ve recently completed rewriting our chat application for work with React.js and it is, in my opinion, a really neat library for building modern web applications.
That said, I’m not here to suggest that other frameworks and / or libraries suck. Give React a try, but at the same time, take a look at Angular, Ember, Backbone and others that strike your fancy.
Evaluate their design principles, understand your application’s needs, consider your team’s diversity and then go with the one that suits you best. Have fun!