In this lesson we'll shift the responsibility of managing the state of our application over to a higher-order component rather than having each component managing it's own state.
[00:00] Now that we've created our first few components in our application, we've actually exposed a problem, that this pattern, right here, needed a constructor and a state callback. Component WillMount and Unmount and onChange handlers is going to get really, really tedious. We're actually going to need this functionality in a lot of places.
[00:19] If we look at our completed application, we're going to need it here in our catalog item to keep track of how many of those we have. We're going to need it on this product page, same functionality there. We're going to need it up in our cart summary and we're also going to need it in our actual cart.
[00:35] To resolve this, right here in our mixins directory, we're going to create a new file. I'm going to call it store watch mixin.js. Now, ES6 classes do not support mixins. I'm only calling it mixin because I feel that'll be more familiar to you guys, but what we're going to create is a higher-order component or function.
[00:58] Here, we're going to need React. We're also going need our app store and we're going to export by default a function that takes in an inner component, so this'll be whatever component we pass to it and a state callback. We'll take a look at that in just a second, but what this guy's going to return is a new React component.
[01:18] The inner component again is the component that we pass in and that state callback is the function that we're using to get our component's initial state. If we look over at app cart, that function is going to be cart items and, of course, the inner component is going to be cart.
[01:33] What we're going to do is copy all of this right out of our cart, and we're going to drop that right into our higher-order function. This is going to take in the props that our component may have. Then our state is going to be set by state callback.
[01:48] We're going to take the opportunity here to go ahead and pass our current props into the state callback. It's a great chance to reuse any props that may exist in order to determine our component's initial state. Then all this can stay the same except for this.setstate and the onChange is going to use state callback and this.props.
[02:12] In the render method of our new component, we're simply going to return our inner component, so in our current use case, that's going to be our cart. We're going to use the spread operator to pass in this.state as props and we're also going to carry over any props we may have started with. That's looking pretty good.
[02:34] We can jump back over here to our cart and we're no longer going to need any of these events. We're going to convert our cart into a stateless function component. So cart equals a function, takes in props and returns our object.
[02:53] We don't need render any longer. So we can go and clean all this up. We're calling this.state, that's going to be props. Of course, we're going to need to bring in our store watch mixin. Then finally, we're going to wrap our default export in our mixin, pass in on cart as well as our state callback, which in this case is cart items. We should be good.
[03:22] Let's go and check out our application and the state of our cart is working. It's being managed by our higher-order function. We can quickly implement this same pattern in our catalog by bringing in our store watch mixin and then we'll convert our component to a stateless component using the same pattern from our cart so it takes in props, returns our object.
[03:50] We don't need the constructor and we don't need our render method any longer where we're using this.state. We'll simply have props and it'll wrap our default export of catalog in our store watch mixin, catalog being our inner component and good catalog being our state callback. We'll save that and check out our app. Refresh this just to make sure. Everything seems to be working just fine.
[04:25] We'll jump over to our catalog item where we can now implement another feature that our application needs since our store watch mixin is keeping track of the state of our catalog component. We've got a span here with a class name of text. Success. To get that nice screen from bootstrap and inside of that guy, we're simply going to say props.item.quantity, so if we do have that, we'll use ES6 string interpolation here wrap in parenthesis are props.item.quantity and then just the phrase "in cart."
[05:05] I'll save that. Check out our application one more time, and now when we add an item to the cart, our catalog item is able to keep track of that. If I reduce this, it reduces here, so we're looking at widget2 which has three in cart and if I remove that, all the other components in our application are aware of the state of our application, thanks to the higher-order of function.
It all happens here in app-store.js
getCatalog(){
return _catalog.map(item => {
return Object.assign( {}, item, _cartItems.find( cItem => cItem.id === item.id))
})
},
We map cart item's qty onto the items in a clone of _catalog
Thanks, this cleared it up for me!
getCatalog(){
return _catalog.map(item => {
return Object.assign({}, item, _cartItems.find(cItem => cItem.id === item.id))
})
}
Just wondering if I've written this code down wrong and can't see the mistake because I can't seem to merge the data and get the 'id' property to show up on the '_catalog' data. It exists in the '_cartItems' when I add one to cart.
At 2:06 you replace a function with an invoked function, why does it still work? Also, on line 7 you simply pass in props but on line 17 you pass in this.props, why the difference? Many thanks for the great series!
Can you share an example showing how to leverage the higher order component callback for componentDidMount? Would I pass in an additional callback?
In app-catalog, how does the catalog keep up with the quantity of items in the cart? I'm not getting that data on the props (the item.qty) from the getCatalog() function of AppStore.getCatalog(). Thanks.