Table of Contents
- What is this post about? What should you expect and what do I expect.
- #Question 1 What does React being 'declarative' mean?
- #Question 2 JSX: What and Why?
- #Question 3 Why is immutability important when working with React?
- Conclusion
- References
About this post
Welcome to my first post! Ever wondered about the principles driving React? Whether you're a seasoned developer or just curious, you're in for a dive into widely accepted practices within the React community. We're delving deep into fundamental questions, unpacking declarative programming, JSX, and immutabilityโkey aspects that many in the community consider important for React development.
A bit of React, JavaScript, or programming know-how is recommended. These principles, including declarative programming, JSX, and immutability, are widely embraced community conventions and recommendations associated with React.
For years, I've wanted to share insights, aiming for clarity (and maybe taking it easy). Perfection can wait. If you spot quirks or have ideas for improvement, drop a comment below.
Ready to explore React's foundational community-backed principles? Let's dive in!
What does React being 'declarative' mean?
In a general programming sense, declarative programming refers to an approach where through code we declare/describe the objective of our program, the end goal, or in other words, we only tell the answer to the question, "What do we want to achieve at the end?". In the React world, the question would be more like, "What should it look like?".
This can be understood in contrast to what is called the "Imperative programming style," wherein we describe the steps to achieve a task.
In terms of UI, it can be translated into not describing or defining how, when and what DOM mutations we need to do (the imperative style) and instead describing the UI state(s). By a UI state, we mean what the UI should look like when the variables involved or representing the component's state assume certain values.
The Imperative Way
const showElement = ele => {
if(!ele.classList.contains('show')){
ele.classList.add('show')
}
}
const ele = document.getElementById('dummyElement');
if(boolState) showElement(ele)
else hideElement(ele)
// both of the functions called above,
// define the DOM mutations needed to be done
// in order to do what their name suggests
The Declarative Way
boolState ? <Element /> : null
"Why is this great and needed?" you may ask. Well, with this style of programming, we can focus on what our UI should look like, which is the main purpose while developing a frontend. We don't need to burden ourselves with manipulating and cleaning things up in DOM. React does that heavy lifting for us in an efficient and reliable way, perhaps better than if we had implemented it ourselves.
Going forward in software development, more and more of this "how to do something" will be abstracted away from us. Ofc, one downside to this is that we have limited control and access to how the program achieves the result we told it to, but more often than not, frameworks/libraries do it right.
In a nutshell, the declarative-styled code tells you "it should look like this" whereas imperative styled code will tell you "this is what you should do". So when developing in React, maybe you should not think of how you are going to do a certain thing but start with the end in the mind and think about what you want to achieve.
NOTE: In order to keep your React code inline with this philosophy, please avoid executing DOM mutations directly just because you can. It defeats the purpose of React, apart from breaking away or interfering with how React manages the UI.
JSX: What and Why?
JSX or JavaScript XML is a syntax extension to JavaScript created by the folks at Facebook in order to simplify the developer/development experience.
It is a really powerful syntax that eases out the task of creating and manipulating HTML and adding it to the DOM in React.
const element = <h1>Hello, world!</h1>
This funny tag syntax is neither a string nor HTML.
We pass around tree structures composed of HTML and/or React Elements like normal JS values and it creates an HTML element out of it and adds it to the DOM. Every HTML element written in JSX is parsed or converted into a React.createElement call.
By letting us write HTML in React, we can utilize the features provided by React to create dynamic web applications.
Although its not a mandatory requirement to use JSX, it constitutes an important piece in the React Ecosystem. Its creators term it a 'template language that comes with the full power of JS'. It allows us to describe the UI in HTML, making development in React much easier by reducing syntax overhead at dev end.
Using JSX means you will be creating something called as React Component in which the markup and the logic are tightly coupled. These components or units form a loosely coupled way to separate concerns as per the React way, instead of dividing out the markup and the logic into separate files like many other libraries or frameworks.
The React ecosystem insists on organising the application into modular React components. Although React is not opinionated, the philosophy that is generally considered best practise and the one that is encouraged is to divide your application into small, preferably stateless React components.
Bonus: JSX Prevents Injection Attacks or XSS(cross-site-scripting) Attacks. Therefore, embedding user input in JSX is not a worry. Click here for a brief know how.
Why is immutability important when working with React?
I am assuming you are familiar about Immutability in React since virtually every React guide mentions it so I am going to skip ahead. Here, as you go along, you will get to know how the fate of data Immutability and React are intertwined. This little to and fro will help you get an intuition of the why.
React has this concept of props and state variables. From a birds eye view its safe to say, if they change, React simply repaints the DOM by re-rendering the component. React encapsulates the task of transforming the DOM from one state to another.
It cant assume anything about that state since we can update anything and that is why on each state change, it re-renders the component entirely, even if we know its not required.
"We must be calculating a diff right?! We'll know what changed and we are good to go." you say.
Yeaaa, but the problem that arises with that is that props or state variables can be multi-level nested objects.
So, although doable, this means we will have to calculate a diff every time and before each render. It sounds daunting, considering the nesting can be done upto any level.
"No worries. We can do a value check for primitive types and a reference check for others. That'll do the trick," you say.
Not so fast. These object data types are mutable and their reference doesn't change if their values are mutated unlike primitive types. Check this and this out. In that particular order, I might add.
"What do we do now? How do we find a solution to our 'what changed problem'?"
Let's take a step back. If we have somehow solved this problem, this means that the next step for React is to simply repaint the DOM since it has got to know that something has changed. Doesn't this mean that React can still work its way even if it only knows that something has changed instead of knowing exactly what has changed?
"Hmm, makes sense. But we don't want to calculate a diff and the reference doesn't change with mutation, so how would React know the data has changed?"
It won't on its own. That's why we will supply it with a new reference whenever we make a change, just like what the idea of data Immutability describes. Sending a new object that will have a different reference but all the values of the previous variable along with the changed value makes it easier to tell that something has changed than to actually compare the two objects and look for a difference.
In order to avoid deep equality checks to figure out if the state has changed, it does shallow checking, which means that if we don't, supply a new reference, the state change might not affect the UI since from React's POV, nothing has changed.
This way, React stays blind to the actual low-level changes you make, but is only worried about whether you made a change or not.
There are ways, like the shouldComponentUpdate life-cycle method or using the second arg of React.memo HOC, to enable or implement deep equality checks that are employed explicitly in order to mostly improve performance when we know for sure that shallow checking is causing many more unwanted renders.
Immutability can also help make the code more explicit when data changes occur.
const object = {
x: 2,
y: 4
}
const changedObject = performSomething(object)
object.x
object.y
changedObject.x
changedObject.y
Creating a changed copy because we are following the immutability approach, has marked the code that some operation has been performed and the values have changed.
It also makes, retrieving the older state easier.
How do we achieve Immutability in React code in general?
For objects, we can use Object.assign or the newer Spread syntax. If the value you have to change is a nested one, you have to 'spread' your way to its level. For arrays, we need to either return a new array or find methods that update the arrays in an immutable way rather than in place.
Some of those methods are:
- Array.slice
- Array.map
- Array.filter
- Array.concat
To Avoid: unshift, shift, pop, push, splice
Instead of using sort directly on your original array, sort a newly created copy of the array.
Yes, by implementing immutability, we are constantly creating new objects and arrays all the time.
This has a performance impact of its own, but it also boosts the apps performance. We need to experiment at a more detailed level to find out which one wins, although it also depends upon how the things are built.
Libraries like Immutable.js have tried to bring the efficiency of working with immutables closer to that of mutables, so that's a relief if creating new values all the time is giving you stress.
React also provides tools to work with immutable data structures and improve your application's performance. Immutability helpers and mixins(not recommended though) are a few examples.
Cons:
Adds to dependencies and maybe a little more code.
It adds to the dependency since native idioms (the inbuilt methods we listed above) used to implement immutability are not very performant and we generally need external packages to implement this concept to the letter.Immutability can have a detrimental performance impact when the dataset is small, since computer architecture is desgined to mutate the data in a direct way.
Inconsistency
Since its an idea and not a directly enforceable thing like code formatting through linters, it depends upon the developers to implement it with discipline and in a standard way. Because there is human factor overhead, consistency can break.
Conclusion
I hope you learned a little bit about React and its philosophy through this post. I initially thought of covering one more topic of 'Using custom hooks to fix prop drilling instead of using the Context API or a state management library' but maybe later.
Also, I originally came across some of these questions through a comment on an article I was reading where the guy was outlining what he would ask or like to be asked in a React interview. I got wondering myself and as I finished my quest of finding the answers, I thought why not make a post.
Feel free to comment down some of the other questions that you think are worthy enough to be added to this list acting as a yardstick to checkout developers and making us feel closer to React.
Until next time, guys. See ya!
References
https://reactjs.org/docs/introducing-jsx.html
https://www.freecodecamp.org/news/what-the-heck-is-jsx-and-why-you-should-use-it-to-build-your-react-apps-1195cbd9dbc6/
https://egghead.io/learn/react/beginners/wtf-is-jsx
https://danburzo.github.io/react-recipes/recipes/immutability.html
https://reactkungfu.com/2015/08/pros-and-cons-of-using-immutability-with-react-js/
https://github.com/kolodny/immutability-helper
https://www.dottedsquirrel.com/declartive-imperative/
Top comments (0)