Writing React Code: Sage Mode

Tejas Upmanyu
16 min readOct 16, 2018

--

Best practices and lessons learnt while writing React code.

Code is like humor. When you have to explain it, it’s bad. — Cory House

After a few successful React experiences and a couple of bad ones, from writing and seeing bad React code to now being a little (yep, just a little) considerate about what I write, I’d like to share the techniques to enter Sage Mode with React, sharing the timeless principles of writing quality code at scale.

React isn’t easy to take up & React is tough to let go of.

That statement describes well my experiences with React over past couple of months. From a beginner to now being an established beginner 😅, I’d like to share my learnings and how I progressed from writing just code in React to writing code — sage style in React. (Being a big Naruto fan, all my childhood, this is my moment of epiphany and of course, weird references are hidden everywhere, welcome 🙌🏻). Here, I inundate my 10 ways to achieve sage mode in React. This article, is not about how to write React code but about how to write better React code and win maintainer/reviewer’s hearts 💝

1. Destructuring & Spread Operator

I know, that you know about destructuring but when are you going to do it, pal?

Writing clean code is not easy and as your code base starts to expand, it becomes an uphill battle. As your props increase and the number of objects in your code increase with expanding depths, your code starts to look more and more bulky like this -

While the one reviewing your code might be in for a headache, destructuring can definitely help turn your codebase clean and let your reviewers be at peace, like -

Let’s spread em’

Spread operator introduced with ES6 can well replace the verbose —

Object.assign({}, object1, object2); // less cooler 🙁

because now you can spread em like this —

{...object1, ...object2} // way cooler. 😎

Isn’t that neat?

2. State Mutations & Arrow Functions

Do not mutate React state,

if you want your Facebook account safe.

— Mark Zuckerberg.

One does not mess with states directly in React, you would have heard the phrase ‘state must not be manipulated directly’ a gazillion times yet knowingly or unknowingly many of us tend to mutate state and have a wonderful sleep at night. React Component API’s setState() method is to be used to alter state values and that too with care. I repeat, never manipulate the state directly. A good practice is to treat state as immutable. Let’s take a innocuous example of state mutation that can result in some serious damage,

/* assume 'numbers' is an array in component state --[1, 2, 3] */
const newNumbers = this.state.numbers;
newNumbers.push(4);
this.setState({numbers: newNumbers});

seems alright, everyday code, doesn’t It? Well, not so much. The problem lies with the fact that in Javascript, Arrays and Objects are passed as reference 😱This means that when we assign newNumbers as this.state.numbers, newNumbers now refers to the original memory location where state values are stored. And then wreaks havoc as we push into the array, causing mutations in state without using React’s state management utility setState() can put you in all sorts of troubles, as we see in the very first line when you try to use setState(), the results are not what one expects. Solution? Beware while using Arrays and Objects with state mutations. A safer approach is to always create a copy of state variables and then perform mutations on that copy, assigning it back to state using setState(). These are mistakes we make unwittingly and the strange thing is that we don’t get caught very often, until something breaks colossally. So, take the words of wisdom and never mutate state directly. 👍🏻

Arrow Functions => When to use and when not to?

Ah, ES6 gave us a lot of cool stuff, some of that gold dust was arrow functions or fat arrow functions as some might refer to them, calling them fat is just body shaming BTW. So minding the subject, arrow functions are similar to lambdas in Python and replace anonymous functions in Javascript. The upsides are shorter and cleaner syntax with no hassles of binding and they don’t have their own ‘this’ instead they inherit it from the parent, sounds cool. Let’s use arrow functions everywhere then……… uuhhhh no 🚫. As every engineered thing, arrow functions also have their downsides and using them everywhere can result in some pretty mysterious bugs. Arrow functions don’t have their own lexical scope hence no own ‘this’. Arrow functions can’t be used as constructors as well and while they are an excellent choice for callbacks and anonymous functions, please please restraint yourself from using them as class methods. 🏆

3. Variable/Function Names

Do give it a thought before writing those random and totally absurd variable/function names, unless you are sure nobody is gonna read that crap and you don’t write crap, do you? — The Punisher

I am absolutely sure that you’ll find this point in every clean code manual ever written and any good code thesis ever presented and you my friend have heard it a million times, yet you tend to write poor variable/function names just to save some seconds? Your code is not your own, it is supposed to be read by one too many people and while we live in a connected world, I am skeptical even if your best friend will understand your intentions behind supremely elaborative variable names like a, data and what not. You know it, don’t you that just to shave off a few seconds you write less descriptive/ totally absurd variables and functions which eventually end up taking more time on the readability and maintainability side of things. Every time you write a variable or a function in a big codebase, think about that poor kid out of college who is probably going to end up maintaining or extending your legacy, your code and while popular extensions like Git blame and Git Lens will allow him some solace of cursing you, but that’s not really how you want it to be, isn’t it?

Write code, such that people don’t think about using GitLens or finding you and hacking you to pieces. 👨🏻‍💻

4. Derived State

Think of component state as real-e-state, don’t create them if you can do the job without them, unless it is absolutely necessary.

— Professor Snape.

State of a component is meant to store data and values — original, relevant to that component only, It is about storing things that absolutely matter rather than putting everything and anything on the component state. While learning React, I often made the mistake of taking state for granted which resulted in poor quality code where almost everything was being stored in state and subsequent manipulations were eating up on performance. So the burning question is why not use state so often? Well, treat state like precious real estate which you can’t afford, again the question arises why? If you ponder, React’s whole pattern is based on rendering and committing to virtual/real DOM based on changes in value of state and props. I am sure you won’t be onto changing props because you just get them as they are from parent component, on the other hand state is under your control, which often leads to abuses. More state values strongly indicate towards greater number of setState() calls and state reading logic in your code, and what happens when setState is called ? Yeaaaaaaah, re-rendering ….correct ….and rendering is a costly process as you might now. You don’t want to call render when it can be avoided by use of data or values from props, isn’t that wise? Too many setStates, too many re-renders (which could have been avoided) and are we even talking about performance here ??? Performance and code quality take a strong hit.

Often times we end up storing or deriving state values from props, such state is called Derived state. If you apply your grey matter to it, there are definite ways of avoiding derived state, though I don’t recommend derived state, there are circumstances where you can’t do anything without it, don’t worry you’ll be absolved of your sins, if you are sure that the derived state is absolutely necessary. There are a couple of React lifecycle methods concerned with this use such as componentWillRecieveProps() (UNSAFE), getDerivedStateFromProps() and some other rarely used ones. Here is an excellent article from the React team on why you should and probably can avoid derived state. Do check It out.

5. Error Handling & Fallbacks

Be paranoid about your code, It can break anytime, anywhere. — Mojo JoJo

We’ve been going about the term — error handling for years now yet for most of us error handling comes as an afterthought and for some — never. You don’t realise that your code can break until it breaks on your face and then we look for ways to put band aids over the errors. To write better code, error handling should not be an afterthought, but an integral part of your development exercise. Fallbacks are referred to actions to perform in case an error occurs and something breaks, what do you want your software to behave like when hell breaks loose. Error handling is not an easy process, you can do it for the component you write but what about the ones others have written, and say you tested them well, still errors can pop up on integration points between various parts of a project. Thus, you need to have a well planned and granular error handling scheme in place for your whole project. Personally, Error boundaries in React have been of great use and provide a way to handle errors gracefully on the global scale.

Myself and a couple of others have written a lot of code like this —

const userAge = this.props.user.userInfo.age;

which is all cool, until suddenly your props’ user object doesn’t have a userInfo field and hell breaks loose with a shiny error on the console — can’t find ‘age’ of undefined. This is a quite simplistic example of what I want to convey here, on a grander scale, when we write code like this — you don’t know where and when is it gonna break. Our rock solid confidence on the existence of certain fields like userInfo in the above example can lead to a console full of ‘undefined’ clamouring — one thing web developers never get delighted to look at. My two cents here would be to be aware of what you are writing and are you 1000% sure that all the fields you are accessing usign dot operator will exist at all times. If not, treading carefully never hurts. Suppose I am sure that props will always have user object, but I am not so sure about userInfo, I can write something like this to avoid the ‘undefined’ pitfall and sleep peacefully. Sure, this looks a bit verbose and you can probably write a utility function or get one from popular libraries like lodash for the task but for example’s sake, this looks solid.

const userAge = (this.props.user.userInfo && this.props.user.userInfo.age) || 0;

6. Class vs functional components

Do you really need those classes and states?

Using classes is not bad, It is just an overkill in some cases

A plethora of times, I’ve seen people and yours truly, going out in the wind and creating a new component with a class extending React’s Component API, before they even think about what they want to do — they just create it. Now the question is why? What’s the problem? Yep, No problem except the fact that your probably don’t need a class and what is called a stateful component — probably functional/stateless component will suffice your use case. So why that extra baggage?

Functional components suffice some times.

7. Unit Tests

Write test cases like your code is going to be deployed into a NASA space mission. 🚀

Its not unnatural to take unit tests lightly in the beginning, but if you are still carrying that feeling, drop it mate. There are thousands of resources on the internet to teach you how to write unit tests, but what I am gonna talk about is why you should take them seriously and as an undivided aspect of your development procedure. What I’ve felt from the time test cases didn’t exist for me to now when they take an important seat is that test cases are not just about ensuring that your code is not breaking, the side effects of writing good tests are well rewarding.

For me, testing is a way of keeping your code streamlined with expectations, it helps you learn the intricacies of what you’ve written and come clear with what your code does, exactly. Believe it or not, testing is also a way to document your code from a programmers perspective as it gives a bird’s eye view of what you expect from your code. Testing also plays a major role in helping you debug in case of regressions and other bugs, as you get to know how and where your code is breaking. Easily testable code is also good code, and easy to maintain. Remember if you can’t test it, you probably can write better than that. Where % code coverage also matters but what really matters in my opinion — is the quality of test cases you write. It doesn’t matter if you run tests to count elements in you React code, it will be successful most of the times. What really matters is the inside stuff — the functionality your code wraps, in terms of functions — do test them out in every way you possibly can because that’s what your tests should be about, ensuring that your code functions the way it is intended to.

If the project circumstances allow it, go for TDD (Test Driven Development) if you are able to do it or not, It’ll be a lot better than what it was. And if TDD is not yet in scope, do take unit tests seriously and the favour you can do to the project is by testing your components extensively.

8. Extensibility, Modularity & Reusability

Heard them over and over…

Hardcoding values into your code is a noob mistake which all of us have done in our initial programming days but the path to enter sage mode begins with being more considerate about what you write and how you can do it better. Creating constants out of values is one minor step into a major software engineering goal termed Extensibility. Your software is not gonna be like that forever, every code is to be changed, manipulated and extended to suit the needs of that time. Extending your code can be a royal pain until you write code anticipating future changes and ensure that your code can be ready for extensions with minimum changes in other places. So making changes should not take a maintainer into an endless puzzle, draining them of energy and precious time. In short, focus on extensibility with appropriate measures in place to make the process of change easy on whoever starts on that journey.

A major part of a maintainer’s dream is code comments, and while extensibility is a long term goal — code comments make their importance felt rather early and painfully. In my personal preference, start focusing hard on comments as well as quality of comments as your code exceeds 75 lines. Its not about just putting random jargon as comments, comments should be brief as well as descriptive enough so that when in future someone else or you start revisiting the code, It should come up to you as a story, free flowing and — explaining itself at every step.

Modularity

Talking about Modularity, modularity along with others becomes alarmingly important with your code base increasing. No one likes to see 50 line function or a 1000 line component. Cutting down on your code into multiple files, smaller functions can definitely help you abstract out similar logic together. Personally, I use this scheme for a file from top to down—

Absolute imports, relative imports, style definitions, constants, core logic functions, utility functions…

but It is fair to abstract constants and utilities into separate files with only the minimal business logic residing in the component. This approach should be carried all over the project to carve out a modular and well defined structure to the project, enforcing extensibility and modularity at the same time.

Reusability

It is important to pay respects to Reusability as it is one of defining characteristics of React. Reusable components, Reusable everything 🤩. Before writing any code or functionality — ask yourself — can this be used somewhere else with little to no changes? If the answer is yes or even if It isn’t try to write functions upholding the generic nature. Keeping functions and components generic not only improves upon quality but gives boost to reusability. I agree, keeping things generic is tough 😢 and that’s where you need to make the call — to what degree are you thinking of making your function/component generic and is the time spent to make it more generic worth it? Because spending days on writing a function so generic but nobody ends up using it to its potential is a great waste of time. So try to keep things generic but at the same time take strong calls on till what point are you ready to dive in and are the efforts required to enhance on the generic nature of code worth it. My experiences with writing generic, reusable components has been struggling and delightful at the same time — the joy of writing code which will be put in active use by others as well as the attached responsibility…..Its all too good and sincere a job, as it is a learning.

9. Craftsmanship Matters

You are not just another worker, you are a craftsman! The world is waiting for your masterpiece — function keeping that at the front of your head.

In today’s world, rarely would you be getting time to write the ‘perfect’ code or even anything closer to that. In my views, there is nothing called ‘perfect’ code. There is always room for refactoring and polishing your code. As people say ‘perfect’ code is an illusion, there are flaws and being the writer it is our responsibility to account for and minimise those flaws. There will always be tight deadlines, pressure situations, exasperating client pressure, fatigue, fever and what not but these situations don’t absolve you from writing solid code. Taking your work as that of a craftsman, however dreadful the times be and however early you need to ship it, don’t compromise on your creations. Whatever be the scene, you always have a choice — either to take shield of the numerous excuses and ship bad code or to take ownership of it and deliver delight. A code that works is not done, its never done. There are times when your reviewers/clients are unable to decide or are happy with what you’ve worked up but that isn’t the end of it, if you know it is not perfect, keep working in the shadows to make it or drive it close to what you take as perfection. The ends don’t always justify the means, and even if it solves the toughest of problems, how it solves it should be of utmost concern for you. For most, Its just enough to make it work but that shouldn’t satiate you because you are not just another worker — you are a craftsman! Be obstinate about high quality and performance. Remember that work done is rewarding but work done well is soul food. And however good it appears to be working right now, well crafted code always stands ahead of others in the long run. Mind that simplicity is the ultimate sophistication, writing overly engineered/decorative/hacky code is not craftsmanship — while it might be fun for you, if takes 3 lines and is more readable then it is better than that fancy one liner you wrote.To conclude, take moral responsibility of what you are writing/delivering, go the extra mile to make it better.

10. The Art Of Being A Developer — Facing Criticism

Progress > Perfection. Never take a fellow colleague’s or any one else’s comments about your code in a negative sense, Instead be thankful for them inadvertently trying to help you improve. Facing criticism is the hallmark of greatness.

— Spongebob

Being a developer = Being a student all your life. 📚 Taking criticism positively and as stepping stones to improve is the hallmark of greatness. Life is short too make all mistakes on your own, so be eager to learn from other’s experiences and suggestions. Since my early days at programming, I’ve taken criticism in good spirit and that has also been a strong motivator to improve continuously. Bow down to greatness and be grateful for all the help and support you get in your journey. Be thankful to the people who remind you of your shortcomings every now and then whether nicely or harshly, they help you to move at blazing fast speeds on the learning ladder. While these might seem as feel-good words, I am totally convinced when I write them. Praise takes you 2 steps ahead, criticism takes you 20 (ahead or behind, only you can decide that). Appreciate the people who’ve been behind you, supporting you at every step and never be shy to express gratitude. Believe in progress, however slow and small that might be, it definitely counts. Believe in delivering stellar quality, whatever be the scenario. Contribute back to the community in whatever way you can and never have a shred of diffidence in your abilities. Neither you nor your code is weak, just different and in need of refinement. Live a solid life, and that will make all the things around you good. There’s nothing bad in being a pedant and having a penchant for good. Believe. 🙌🏻

Who sows virtue, reaps honor.

Ending Notes

If Spongebob can light fire underwater, you can do great as well.

Most of the points mentioned here were the ones you already knew, right ? If there were ones you didn’t, thank you. The real fact of the matter is that even after knowing all this, do we live by it? If we don’t now’s the time to awaken your sage mode 😇 with React. Go, deliver greatness. All the best 🎉

It is my first article on Medium 🤩 Thanks for reading! Hope you enjoyed and took something from my experiences with React.

--

--