6

Transferring Properties

Working with properties has a frustrating side. We saw a bit of this side in the previous chapter. Passing properties from one component to another is nice and simple when you’re dealing with only one layer of components. When you want to send a property across multiple layers of components, things start to get complicated.

Complication is never a good thing, so in this chapter, let’s see what we can do to make working with properties easy across multiple layers of components.

Problem Overview

Let’s say that you have a deeply nested component, and its hierarchy (modeled as awesomely colored circles) looks like Figure 6.1.

A red circle is present at the top of the hierarchy. A yellow circle is present below the red circle. This yellow circle branches into a green circle and a blue circle. The green circle branches out into two purple circles.

Figure 6.1 The component hierarchy.

You want to pass a property from your red circle all the way down to the purple circles, where it will be used. What you can’t do is the very obvious and straightforward thing shown in Figure 6.2.

The figure shows the hierarchy represented by the colored circles, with an arrow from the red circle at the top to one of the purple circles at the bottom. An exclamatory text “Woot” is mentioned adjacent to the arrow indicating that we can’t pass a property directly to the component we want to target.

Figure 6.2 Can’t do this.

You can’t pass a property directly to the component or components that you want to target. The reason has to do with how React works. React enforces a chain of command in which properties have to flow down from a parent component to an immediate child component. This means you can’t skip a layer of children when sending a property. This also means your children can’t send a property back up to a parent. All communication is one-way from the parent to the child.

Under these guidelines, passing a property from our red circle to our purple circle looks a little bit like Figure 6.3.

Every component that lies on the intended path has to receive the property from its parent and then resend that property to its child. This process repeats until your property reaches its intended destination. The problem is in this receiving and resending step.

If we had to send a property called color from the component representing our red circle to the component representing our purple circle, its path to the destination would look something like Figure 6.4.

The hierarchy of the colored circles is shown. Three arrows: from red to yellow, from yellow to green, and from green to the purple circle are shown to indicate that the property is passed from parent to child.

Figure 6.3 The property is passed from parent to child.

The color property is sent from the red circle to the purple circle.

Figure 6.4 Sending the color property.

Now, imagine that we have two properties we need to send, as in Figure 6.5.

Two properties: color and size are sent along the hierarchy of circles.

Figure 6.5 Sending two properties.

What if we wanted to send three properties? Or four?

You can see that this approach is neither scalable nor maintainable. For every additional property we need to communicate, we have to add an entry for it as part of declaring each component. If we decide to rename our properties at some point, we have to ensure that every instance of that property is renamed as well. If we remove a property, we need to remove the property from being used across every component that relied on it. Overall, these are the kinds of situations we try to avoid when writing code. What can we do about this?

Detailed Look at the Problem

In the previous section, we talked at a high level about what the problem is. Before we can dive into figuring out a solution, we need to go beyond diagrams and look at a more detailed example with real code. We need to take a look at something like the following:

Let’s take a few moments to understand what’s going on. Then we can walk through this example together.

We have a Shirt component that relies on the output of the Label component, which relies on the output of the Display component. (Try saying that sentence five times fast!) Figure 6.6 shows the component hierarchy.

The component hierarchy shows the "DOMReact.render()" function at the top, followed by the "Shirt," "Label," and "Display" elements present under it, one below the other.

Figure 6.6 The component hierarchy.

When you run this code, the output is nothing special. It’s just three lines of text, as shown in Figure 6.7:

Three lines of text: "steelblue," "3.14," and "medium" are displayed one below the other in an ordinary fashion. A label indicates that the output is rather boring.

Figure 6.7 What our code outputs.

The interesting part is how the text gets there. Each of the three lines of text that you see maps to a property we specified at the very beginning inside ReactDOM.render:

The color, num, and size properties (and their values) make a journey all the way to the Display component that would make even the most seasoned world traveler jealous. Let’s follow these properties from their inception to when they get consumed. (I realize that a lot of this will be a review of what you’ve already seen. If you find yourself getting bored, feel free to skip on the next section.)

Life for our properties starts inside ReactDOM.render when our Shirt component gets called with the color, num, and size properties specified:

We not only define the properties, but we also initialize them with the values they will carry.

Inside the Shirt component, these properties are stored inside the props object. To transfer these properties on, we need to explicitly access these properties from the props object and list them as part of the component call. The following is an example of what that looks like when our Shirt component calls our Label component:

Notice that the color, num, and size properties are listed again. The only difference from what we saw with the ReactDOM.render call is that the values for each property are taken from their respective entry in the props object instead of being manually entered.

When our Label component goes live, it has its props object properly filled out with the color, num, and size properties stored. You can probably see a pattern forming here. If you need to let out a big yawn, feel free.

The Label component continues the tradition by repeating the same steps and calling the Display component:

Notice that the Display component call contains the same listing of properties and their values taken from our Label component’s props object. The only good news from all this is that we’re almost done here. The Display component just displays the properties as they were populated inside its props object:

Phew! All we wanted to do was have our Display component display some values for color, num, and size. The only complication was that the values we wanted to display were originally defined as part of ReactDOM.render. The annoying solution is the one you see here, with every component along the path to the destination needing to access and redefine each property as part of passing it along. That’s just terrible. We can do better than this, and you’ll will see how in a few moments.

Meet the Spread Operator

The solution to all our problems lies in something new to JavaScript, known as the spread operator. What the spread operator does is a bit bizarre to explain without some context, so let’s first give you an example and then bore you with a definition.

Take a look at the following snippet:

We have an array called items that contains three values. We also have a function called printStuff that takes three arguments. We want to specify the three values from our items array as arguments to the printStuff function. Sounds simple enough, right?

Here’s one really common way of doing that:

We access each array item individually and pass it in to our printStuff function. With the spread operator, we now have an easier way. You don’t have to specify each item in the array individually; you can just do something like this:

The spread operator is the ... characters before our items array. Using ...items is identical to calling items[0], items[1], and items[2] individually, as we did earlier. The printStuff function will run and print the numbers 1, 2, and 3 to our console. Pretty cool, right?

Now that you’ve seen the spread operator in action, it’s time to define it. The spread operator allows you to unwrap an array into its individual elements. The spread operator does a few more things as well, but that’s not important for now. We’re going to use only this particular side of the spread operator to solve our property transfer problem.

A Better Way to Transfer Properties

You just saw an example of using the spread operator to avoid having to enumerate every single item in our array as part of passing it to a function:

The situation we face in transferring properties across components is very similar to our problem of accessing each array item individually. Allow me to elaborate.

Inside a component, our props object looks as follows:

As part of passing these property values to a child component, we manually access each item from our props object:

Wouldn’t it be great if there was a way to unwrap an object and pass on the property/value pairs just like we were able to unwrap an array using the spread operator?

As it turns out, there is a way. It actually involves the spread operator as well. We explain how later, but this means that we can call our Display component by using ...this.props:

The runtime behavior when using ...this.props is the same as when specifying the color, num, and size properties manually. This means our earlier example can be simplified as follows (pay attention to the highlighted lines):

If you run this code, the end result is unchanged from what we had earlier. The biggest difference is that we are no longer passing in expanded forms of each property as part of calling each component. This solves all the problems we originally set out to solve.

By using the spread operator, if you ever decide to add properties, rename properties, remove properties, or do any other sort of property-related shenanigans, you don’t have to make a billion different changes. You make one change at the spot you define your property. You make another change at the spot you consume the property. That’s it. All the intermediate components that merely transfer the properties remain untouched because the {...this.props} expression contains no details of what goes on inside it.

Is this the best way to transfer properties?

Using the spread operator to transfer properties is convenient, and it’s a marked improvement over explicitly defining each property at each component as we were originally doing. The thing is, even the spread operator approach isn’t a perfect solution. If all you want to do is transfer a property to a particular component, having each intermediate component play a role in passing it on is unnecessary. Worse, it has the potential to be a performance bottleneck. Any change to a property that you are passing along will trigger a component update on each component along the property’s path. That’s not a good thing! Later, we look at ways to solve this transferring properties problem in a much better, without any side effects.

Conclusion

As created by the ES6/ES2015 committee, the spread operator is designed to work only on arrays and arraylike creatures (a.k.a. something that has a Symbol.iterator property). The fact that it works on object literals such as our props object is a result of React extending the standard. No browser currently supports using the spread object on object literals. Our example works because of Babel. Besides turning all our JSX into something our browser understands, Babel turns cutting-edge and experimental features into something that’s friendly across browsers. That’s why we’re able to get away with using the spread operator on an object literal, and that’s why we’re able to elegantly solve the problem of transferring properties across multiple layers of components.

Now, does any of this matter? Is it really critical that you know about the nuances of the spread operator and how it works in certain situations and doesn’t work in others? For the most part, no. The important part to realize is that you can use the spread operator to transfer props from one component to another. The other important part to realize is that we will look at some other ways in the future to make transferring properties equally simple, without running into any performance issues.

Note: If you run into any issues, ask!

If you have any questions or your code isn’t running like you expect, don’t hesitate to ask! Post on the forums at https://forum.kirupa.com and get help from some of the friendliest and most knowledgeable people the Internet has ever brought together!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset