A Village That Forbids The Presence Of Men

Umoja land where men are banned

Smartphone

独家优惠奖金 100% 高达 1 BTC + 180 免费旋转




Smart and Dumb Components in React

The beauty of the solution above starts to appear when you have to move this component a lot, or remove some pieces from it (or move part of this component to another). Basically, all sorts of possible business changes, which we face all the time in our frontend applications — you know, pace has never been so fast.

Also, because we need to somehow make these components flexible, we need to pass data to them, and here it becomes more complicated. Originally (and which is still true), React was standing only for “V” (view layer), so we had to deal with external state on our own. Also, there is a mechanism inside components called “state”, which works, but does not really scale (you can do it, but general consensus is that there are easier ways). Soon after discovering all that issues, Facebook announced Flux architecture — unidirection data flow, which was pretty different from usual two-way data binding at the moment in the industry.

Flux and it’s successors (Alt, Reflux, Nuclear, others, and finally Redux, which is a de-facto standard nowadays) popularized and made it very prominent, that state should be contained outside of the views; and developer decided how and where do we connect this external state to our application, and I will show you 3 different approaches:

This is the most verbose way, but also the most explicit — there is absolutely no magic, we just pass store object down the component tree, and each components render based on what data they need.

And welcome message itself might look like the following (with performance optimisiations applied):

Important thing here is that “dumb” components are not allowed to know anything about the store, so WelcomeMessage from the previous example should not receive the whole state, rather just a name string. So, the component will look like the following:

It’s true that the result component is more flexible — instead of any knowledge about store implementation, we just pass name property explicitly, which helps us to render this component with arbitrary names.

The biggest problem in this case is the necessity to pass this parameter, so the “smart” component should explicitly know about need of each “dumb” component. It is fine in the beginning, but as soon as we reach several levels of nesting components, it becomes pretty tricky to track what exactly do we need to pass down, and, moreover, it is even trickier when we want to remove some part of the application, but inside another dumb component. So we can easily keep passing unnecessary property, which we forget to remove from propTypes because of the nesting.

The answer to this concern is that we don’t necessarily want as little smart components as possible — if there is a big chunk of reusable UI (let’s say, calculator), don’t be afraid to make this one a smart component as well — it will make removing calculator from one page and adding it to another much simpler.

This is a very similar approach as it was shown in the previous section, but with applied advice from the end it to extreme. It means that as soon as we need some data from the global store, we have to access it by ourselves — so, a lot of our components become presentational. Of course, basic UI elements, like buttons, loaders, tabs and so on will be “dumb” anyway — it is very rare that we need to adjust their state to some global property; but all that need, should access the store by themselves.

You might ask at this point, “but isn’t it too inflexible to tie all these components to the store?”, and it is a perfectly valid concern. What you do in this approach, if you see that you need to reuse some piece of functionality, you just create a “dumb” component and in your small “smart” component you render it with properties from the store.

So, let’s rewrite our previous component to these two new. I’ll keep them in the same file, again, for the sake of simplicity.

This might be a little bit more verbose, but we still have this advantage of adding and removing components in a very easy manner, which might be a huge advantage, if you need to restructure your views pretty often.

When to use this approach?

I put it to a special section, because you might fire your requests from whatever components you want in any of those approaches, so it makes sense to discuss it separately. By network requests here I mean requests which receive data for all those components on our page, and here I see two main approaches.

The first one tells that we need to fetch all needed data at the top component (usually the top component of the given route). It does not really matter how we will retrieve this data later — either in the top component as well, or inside the individual components. The main benefit of this approach is that because it happens in one place, we understand very well what we fetch, in which order, and so on. The downside is that, again, we need to understand precisely what are we going to render, which data is needed, and we need to track that this fetching function is up-to-date to what we render right now (because content of the route tends to change over time).

The other approach is to fetch everything you need inside the component. It means that as soon as component mounts, we fetch everything it needs, and in reality you might easily end up with several identical requests, which were fired from different components. In order to prevent it, you need to build some sort of caching, which will not fire a request again in case it is in the progress, or to be very attentive which components do you render, so they don’t request the same info. It is a very hard task, and this drawback can easily outweight everything else, so if you don’t want to deal with it and have more predictable network requests, fetch everything only in several well-known places.

So, which approach to choose? Unfortunately, there is no right answer — otherwise, everyone would already use it. Choose one which makes sense for your situation and is the most appealing to you and your team — if you are pretty sure about your design, you can go with just fetching all needing info in the top components and passing it down. It will also work even with changing design, but not so big application — if your team is not very big, it is possible to keep all details in your head at once, and change everything during moving components. But if your application is going to be big (or already is), don’t be afraid to rethink what “smart” components are, and how to avoid big cognitive pressure when making any changes — at the end of the day, our goal is to make codebase more maintanable and easy to add new features or change something in existing ones.

Add a comment

Related posts:

Using Kind to test your local changes to Kubernetes

Follow the kind installation guide to make sure you have installed the kind binary for your computer and added it to your PATH. Please make sure you fetch latest tags from the master branch of…

There are always more of them before they are counted

I was reading The Personal Memoirs of Ulysses S. Grant, when I came across a passage that stuck with me. It speaks to how our senses can be tricked. I find it particularly relevant in this era of…

36. Under the Sun

Thainna woke at dawn. She was too excited to sleep any longer. After a brief visit to the bathhouse, she dressed in the nicest tabba that Narissa had sent and braided her damp hair. Did the pale blue…