When we move from CSS to defining styles inside components we lose the ability to override styles with an external style sheet. We also lose the ability add the same class to different types of elements to style them consistently. Recompose allows us to regain the ability of override styles and to apply the same styles to different types of elements.
...So I've created a React app using create React app. I've got this button component here which I'm importing. I have to find it in this file here. It's fairly standard. It's got this button element here. It has an on click handler. It has some styles. These styles are defined below. Then I export this function component as default.
The first problem we need to overcome with our theme is that I don't always want what looks like a button to be represented by a button element. Sometimes I'm going to need the on submit function of a button, but other times I'm going to need the functionality of a link or a span or a div. With CSS, it was easy. I could just apply a class.
How can we do this with React? I'm going to start by installing Recompose. I'm also going to install Radium. Recompose let's us add abilities to components using higher order components or HOCs. HOCs are like power ups for components. They can give them the ability to do things like maintain state or add context or mapping props or lifecycle methods all in a functional way.
The compose function allows us to create a stack of higher order components which we can wrap round an existing component. It's a bit like wrapping layers around an onion. What set display name does is adds a display name to our React component.
Next, we want to create a kind of wild card element which we are going to power up with our enhance function. This wild card element could end up being a div, a button, a span, or a link. We use component from prop to do this. This creates a prop on this component.
Whatever string you pass in to that prop, it's going to create an element with that type. We're going to create a default prop called element. We're going to set its default value to button. Now when I refresh the web view, you can see that I get the normal unstyled button.
But crucially now, I can override this element prop with any element type I want to. Now I'm passing in an A and adding a HREF and it becomes a link.
Back in my component, I'm no longer using this JSX. Let's delete that. I'm no longer applying my styles. Of course I want to change that. I'm going import map props. Map props accepts a mapping function as an argument. In this case, it maps the props from the outer component onto the props which are going to be passed down onto the element.
In this case, we're just going to spread the outer props on. We're going to add in the style. Now our styles are being applied back to our button. I'm just going to change them slightly and put text decoration none and save that.
At the moment, I'm overriding any styles which are passed down from the outer component. What I probably would like to do is to be able to override styles on the component. One way of doing this would be to use an object spread. I could spread in the prop styles and the component styles.
Now, if I go back into my component instance, I should be able to add in a style prop and set the background color to be orange. Save that. Oh, it's not updated. I'm going to go back in here and see why that is. I've actually got the use in the wrong order, so I reorder them because I want the props coming from outside to take presence over the styles here. Save that. Now the styles can be overridden from outside.
But what if we wanted to use Radium for our style so we can have hover states? I'm going to import that here and save that. Then I can put Radium down here. Radium does a deep merge of your styles. If I put that in here, then I can pass in styles as an array, so I can change this to an array. I no longer need to spread in my styles. I can have them flat like this. I'll save that. Now I can use all the features of Radium in my styles as well.
I'm pretty sure I'm going to want to reuse this map props function. Let's factor it out into an A2C. I'm going to import the map props function from Recompose and create a function, add style. That's going to take in a styles object. Then we're just going to pass that down to our map props function. Now we can put add style in here and pass in our styles object. Then import this from our HOCs.
To recap, when we move from CSS to inline styles, we lose some abilities that we're used to. One is the convenience of being able to take a class and simply place it on a range of elements and have them look consistent. We can get around this by using component from prop in Recompose. We use to pass down an element to an underlying component so that it renders out the element that we're wanting.
An additional problem is that our users are used to be able to override our styles by supplying an external stylehseet. When we define our styles inside our component, we gain encapsulation, which is great, but it's a two-edged sword, because we can't override these styles. We can get round this by passing our styles down as props and merging them together with our base styles and then passing them down onto the base element...