در حال بارگذاری ...

Problem: Prop Drilling0:05

In the previous lesson, we built a simple web shop that displays a list of products we can add to a cart and we can manage the quantities both from within the cart overlay and from our overview page. We keep our cart state in our parent component and pass it down to every child component using props. And while props are a fundamental part of Vue, managing state this way can become problematic in larger applications where the data needs to flow down several levels of components.

applications where the data needs to flow down several levels of components. This is called prop drilling and it to be avoided at all times. Taking a look at our example, we have our App component that's the owner of the card state, and then we have a few components that receive cart as a prop. We have our NavBar, we have our CartOverlay. And finally we have a list of ProductCard components.

We have our nav bar, we have our cart overlay. And finally we have a list of product cards. Diving into the cart overlay, we can see we pass the cardState down one more level into the Card component, which will ultimately render CardRows. Let's take a look at the schematic of the data flow. Our App component is the owner of the cardState and this cardState gets passed down as a prop to the child components to our nav bar, to our cart overlay, and to our product carts.

to the child components to our nav bar, to our cart overlay, and to our product carts. Our cart overlay then passes our cart state down one more component to the cart. There's no direct connection between the cart and the app component. And as you can imagine in a larger application, the component tree can extend down even deeper. Now remember in Vue passing down props is a one-way street. A parent can pass down a prop to a child,

Avoid Mutating Props1:37

Now remember in view passing down props is a one-way street. A parent can pass down a prop to a child, but a child is not allowed to modify this prop. And this is by design. If you do try to reassign a prop in a child component view will warn in the console that the target is read only and this this is not allowed. However, if we only update a quantity as follows, view will not warn us as you can see in the console and everything will still update in a reactive way.

view will not warn us as you can see in the console and everything will still update in a reactive way. And even though view doesn't warn, this type of mutation is still considered an anti-pattern because in complex applications, it'll make it very hard for the parent to know which component actually did the mutation. So as a best practice, every mutation of a prop and a child component needs to be avoided. Instead, we can emit an event up to the parent component.

and a child component needs to be avoided. Instead, we can emit an event up to the parent component and let the parent mutate a state. And this is an easy thing to do with a parent-child relationship. For example, the ProductCard will emit an increment and the decrement event and then the parent component or App component is able to do the mutations on the state by calling incrementProduct and decrementProduct. However, we can also mutate a state from within a closure,

by calling incrementProduct and decrementProduct. However, we can also mutate a state from within a carrow, which isn't a direct child of the app component. And this is where we start to notice the problems with prop ruling. Our carrow is a child component of the cart, which in its turn is a child component of the cartOverlay, which finally is a child of our app that manages the state. This means that we need to listen to the events and emit them up again until we reach the parent,

Using Provide and Inject3:11

This means that we need to listen to the events and emit them up again until we reach the parent, which can finally mutate the state. And this is a very clunky way of managing state and it's to be avoided at all times. Let's take a look at a better way to handle state using provide and inject to avoid prop drilling and make working with a larger componentry easier, we can use the provide and the inject API.

and make working with a larger componentry easier, we can use the provide and the inject API provided to us by view. Using provide apparent component can provide a dependency to its component tree and any component inside that component tree can then inject this dependency. Let's refactor our example together. We'll start in app/View. The first thing we'll do is import provide,

Provide Cart Dependency3:49

We'll start in app view. The first thing we'll do is import provide, and then we'll start by providing the cart to our component tree. We can do so as follows, we'll say provide the first argument will be an arbitrary key, for example, cart. And the second argument will be the dependency we'll provide to our components. In this case, we'll provide the entire cart,

provide to our components. In this case, we'll provide the entire cart, but I like to wrap it up in an object so we can easily group multiple dependencies inside this card key. Put more on that in a bit. We'll start by refactoring our nav bar. We will remove our card prop. We'll dive into the nav bar and we can also remove our props definition.

We'll dive into the nav bar and we can also remove our props definition. Next up, we'll import, inject and will say const cart equals inject cart. Finally, let's also make sure to remove every instance of props cart and replace it with cart value. Now this is important because the provided value is a ref, it'll be injected is, and we need to unwrap it manually. Let's take a look at our application.

and we need to unwrap it manually. Let's take a look at our application and we'll see that the indicator in the top right still works. So that's great. Next step, let's refactor our ProductCard. We'll start by removing the card prop. We will pass down. Then we scroll down and we remove our cart definition in the props. I will do the same trick here. Let's say import inject cons,

I will do the same trick here. Let's say import inject cons, cart = inject cart. We'll look for props cart and we'll replace it with cart value. And if all goes well, everything still works. Now, as you may know, our product card does some mutations. We can increment and decrement the product from the cart, and we do so by emitting an increment and a decrement event to the parent.

and we do so by emitting an increment and a decrement event to the parent. And our app component will listen to both the increment and the decrement event and then we'll mutate a state accordingly by calling increment or decrement product. When working with provide and inject, it's still considered good practice to let the parent be in control of mutations. In our case, that's our app component. Our app component defines three mutations.

Provide Mutations to Tree5:54

In our case, that's our app component. Our app component defines three mutations. We have our removeProduct mutation, we have our incrementProduct mutation. And finally, we have our decrementProduct mutation. A cool thing we can do with provide is we can actually provide these mutations to our component tree. We can do so as follows. Let's pull down our provide, let's put it here. And because earlier we used an object here we can just say,

Let's pull down or provide, let's put it here. And because earlier we used an object here we can just say, remove product, increment product and decrement product as follows, back in our ProductCard, we can now import these mutations and this will allow us to refactor our event handlers as follows. Finally, let's clean up our listeners in our Abdo view. We can remove our increment and we can remove our decrement listener.

We can remove our increment and we can remove our decrement listener. If all went well, we can still do our mutations from within our product card. Pretty cool. Finally, we can also clean up our cart overlay. Let's remove our increment decrement and remove event listeners. And finally, let's stop passing down the cart state using a prop where we dive into the cart overlay. We can remove the cart from the prop definition.

prop where we dive into the cart overlay. We can remove the cart from the prop definition. And finally, let's inject our cart. We have to replace prop, so cart with cart, do value, and let's not forget to import inject from view. Now the cart overlay passed the state down to the card component, so let's refactor that as well. We can remove our card prop. We can remove every event listeners actually as well. Let's dive into our card component.

We can remove every event listeners actually as well. Let's dive into our Card component. We can remove or define props, and then we can inject our Card and our mutations. As a last step, we can refactor our event listeners. Instead of emitting, we can now call our mutation directly. Instead of emitting or remove event, we can say removeProducts. We'll pause in the order line. We can do the same with increment and decrement.

We'll pause in the order line. We can do the same with increment and decrement. We'll say increment product order line, and we'll say decrement Order Line. There we go. Now, if all went well, everything should still work. As before, we can increment, we can decrement, and we can remove our product from the cart. Before we move on to the next lesson,

Refactor Summary8:26

and we can remove our product from the cart. Before we move on to the next lesson, let's reflect a minute on what we did. We refactored our App component to provide a couple of dependencies to its entire tree. We provided the cart and we provided the three mutations. We then refactored every component in our tree and used inject to inject these dependencies, and this allowed us to effectively eliminate the prop drilling.

and this allowed us to effectively eliminate the prop drilling. Finally, the parent component is still entirely in control of mutations. We just provide these mutations to every sub component. Every sub component can simply call these mutations directly, allowing for a much more maintainable application and eliminating the need of emitting events all the way up to the parent. And that concludes this lesson.

of emitting events all the way up to the parent. And that concludes this lesson. I'll hope to see you in the next one.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟