Next lesson playing in 5 seconds

  • Overview
  • Transcript

4.6 Challenge: Presentational and Container Components

In this challenge, we’re going to make TwitterAvatar and Profile more portable. They are presentational components, so your task is to write container components to wrap them. Then you won’t need to pass state or actions to them any more.

Related Links

4.6 Challenge: Presentational and Container Components

It's time for challenge 8 where we're going to talk about container and presentational components. So, go ahead and fork this CodePen and let’s go ahead and read some of these instructions. So, it says that TwitterAvatar and Profile are presentational components, right? So we have TwitterAvatar here and profile and these are presentational components. So we need to write container components to wrap them. Then you won't need to pass state or actions to them. So we have a hint here we can use ReactRedux.connect and ReactRedux.Provider. So you wanna do this yourself ,go ahead and pause it. But let's go ahead and solve this. So let's start with TwitterAvatar cuz it's a lot simpler. We can see here it's a presentational component. It takes some properties and it displays something. Now it needs to get these properties from whatever component calls it. So as you can see here TwitterAvatar has to get it's handle passed into to it. What we can do is create a container component which wraps the TwitterAvatar component and it will be responsible for getting that handle from our store here and passing that handle into this presentation component. So we can do this with ReactRedux.connect. ReactRedux.connect takes two functions one that maps states to props and one that maps the dispatch to props. State, of course, being the state that comes from our store and dispatch being a way to send stuff into our store. So both of those can be mapped to props that will be passed as props to our presentational component. In this case, TwitterAvatar does not need to send anything out of itself via function calls, instead it just needs to receive some state that it can render. So what we an do is create a function here called mapStateToProps. And this is going to be a function that takes the whole state of our store, and it returns just the state that we need for TwitterAvatar, because this mapStateToProps function is gonna be used for TwitterAvatar. So what we can do here is just say handle, which is the property we need to pass to TwitterAvatar, and this going to be equal to state.handle. There we go, so that's our MapStateToProps. So now, what we can do, is we can create a TwitterAvatar wrapper, and we do this with the ReactRedux libraries connect method and we can pass it MapStateToProps. And let me go ahead and give us a little more room to work with here. All right, so we pass it mapStateToProps and this returns a function that can wrap our existing component and turn it into a container component. So then we just have another function call here and we pass it TwitterAvatar. So now TwitterAvatar wrapper is a container component because it knows how to get its own state from the store. Now you might be wondering, well, where does ReactRedux.connect get that state so that it can pass it to our MapStateToProps function? And we're gonna get to that in just a second, but it's the other part of our hint here, ReactRedux.Provider. So now, if we were to use TwitterAvatar wrapper, which in a real application you would probably give a nicer name to, or in fact you may not even need to give it a name because this would be what you export from a file. If we were to use this, we wouldn't need to pass it any props such as handle, instead it will go to the store and get those props itself. So how could we use this? Well, ideally, here inside Profile, we'll switch TwitterAvatar here to instead the TwitterAvatarWrapper, and we don't need to pass it any properties. However, if we do that, you can see we get nothing rendered. And if we open the console, you can see why. It couldn't find the store, in either the context or the props of connect TwitterAvatar, which is how React internally represents the Container component. So their instructions are either wrap the root component in a provider or explicitly pass store as a prop to connect to TwitterAvatar. So this tells us what we could do. What we could do here is we could say the store is the store, right? We have our global store here and if we do that this will work just fine. So I could say, tutsplus and Get Avatar and that will work because it knows where to get the store. However, a lot of times you're not gonna have this store as a global variable. It's gonna be inside one function and we need a way to pass it to TwitterAvatarWrapper without actually doing it ourselves. So the better way to do it is to wrap our components in ReactRedux.Provider. So down here where we do ReactDOM.render, instead of just rendering our Profile, lets go ahead and render ReactRedux.Provider. And so we'll just wrap our entire application in this. And then this is where we pass the stall prop, right? So we can pass our store here and normally we would be able to get our store in one place instead of having to set it for every one of our container components which are properly created in their own files. We only have to do this once where we wrap our entire application in ReactRedux.Provider. And as you can see now, if we have this. And let's go ahead and do tutsplus again, and we can get that avatar. Okay, so we've wrapped our TwitterAvatar, but we also need to wrap Profile here. Because Profile requires a couple of properties. It takes the handle and it also takes the handle function. However, the only reason it took the handle was because it was passing it before into our TwitterAvatar but it doesn't need to know about the handle to pass it to TwitterAvatar so we can get rid of it. And really all we need it our change name function. So we don't need any state to map to props, instead, we need to map our store dispatch call here to props. So what we can do underneath Profile here, is write a function which usually is called mapDispatchToProps. And this is a function which takes the store's dispatch method as its property, and we can return an object with the right methods that our presentational component will need. So as we can see, the property name needs to be changeHandle. So let's go ahead and give this a changeHandle function. And in here, we can just copy this line that we have right here. changeHandle is gonna be a function that takes some data and instead of store.dispatch, we just do dispatch cuz that's how it's passed as a parameter here. And we can call our setHandle action creator and pass it whatever data was passed to this changeHandle function. Excellent. So that is mapDispatchToProps. And so now what we can do is create our Profile wrapper. So we can say const ProfileWrapper = ReactRedux. Now normally, we would pass a mapStateToProps as the first parameter. But we don't have a mapStateToProps that we need for this. So we'll just pass null as the first parameter, and then we can pass our mapDispatchToProps. And then we can pass our Profile presentational component as the second function call here, and now we have our wrapped Profile. So now where we render our Profile instead of having to pass it all of the properties that it needs, we can just do ProfileWrapper. And now, let's see. This has refreshed. So let's go ahead one more time and do tutsplus and I can get the avatar and you can see that it works just fine. This is how we can create presentational components and container components. The benefit of having a container components like this is that you don't have to worry about how your data is flowing through your application. We don't have to pass the handle to Profile, which doesn't really need to know about it. It just needs to pass it on to TwitterAvatar. And so this way when you get very deeply nested components, you have to make sure that the props are being passed from one to the other all the way through. Using the ReactRedux.connect function, can bypass this whole part of your application and allow you to immediately send the right parts of your state and the right actions to the components that need it, instead of passing that all around throughout what could be a very complex React application.

Back to the top