Using Redux Reducers in React Components

Mind, blown.

Dan Abramov recently tweeted something that blew my mind!

You can take reducers and use them in your components? NO WAI!

This got me thinking about a coworker’s challenge from last year. He asked if I could make stateless functional components stateful. Long story short, not really, but I ended up creating a very simple Redux library you could use on a per-component basis.

It’s not possible for stateless functional components to be stateful because there’s no way to update a functional stateless component without passing new props. This means, at some point, you have to have a stateful react component somewhere up the chain. Even with React’s Context API, you need a stateful component modifying it’s own state to pass new data to the Provider ’s value prop.

I’d completely forgotten about that code and had quite a bit of trouble tracking it down. It was hidden away in a long list of “write articles on these topics” titled Redux-like State Components. This article is going to cover the concepts of what I wrote and how it allows you to use Redux reducers in React components.

If you’re into component state and the separation of state components and presentation components (think Context API), this solution might be worth looking into. The architecture is pretty similar to the ReduxConnection component I wrote earlier this year.

Redux-like State Components

Disclaimer: I wrote this solution in about 1–2 hours last year and never used it in production. While it functions and works as you’d expect, it’s a very simple minimal-dependencies solution.

Using a State Component

How’s this look exactly? Let’s start with the simplest component, a loading state reducer:

This App component will show “Loading…” when it first renders. 2 seconds later it’ll switch to displaying “Loaded”.

When you look at LoadingState , you really have no way of knowing if this is a ReduxConnection component, Context API consumer, state component, or stateful container component. It follows the same render props patterns as everything else and makes it that much easier to figure out what’s going on.

The setLoading and setLoaded functions are pre-dispatched. Usually you import your actions and then receive a dispatch function, but because all the functionality is contained in this state component, you can call those named functions directly irregardless of the underlying API.

`LoadingState` Component

Since we can’t tell what LoadingState really is, we need to go deeper:

Whoa, looks like Redux right? A major difference is there’s no middleware (at least not in this version), but it looks pretty similar regardless. You create some render props and pass those to a createStateSubscriber function. I exported the reducers and actions since you can unit test those separately.

This does a lot of stuff under the hood compared to Redux in an opinionated way. You don’t directly create a reducer, you create an object of namespacedReducers , similar to createReducers , and createRenderProps wrap all your actions in a dispatch function and manages state for you.

I even wrote it with the notion that one component could handle multiple reducers together. I can foresee a situation where you might want to use a combination of different reducers to have Redux-like functionality.

`FormValidationState` component

In another example, we have this super simple FormValidationState component. It tells you if all your form fields are valid by checking if they aren’t null strings.

Normally, I’d use a form library like Redux-Form, but we’re going to roll our own state component for the purpose of showcasing some more-complex functionality.

In FormValidationState , we have a single action: VALIDATE_FIELDS and a single value on our state: isValid .

Just looking at it, you can see the API. Use the isValid prop to check if the form is valid and call the validation function by passing in an array of fieldNames and the formElement itself.

This is how it looks when you consume it:

When you submit the form, it does a validation check and triggers showing or hiding the error message. Pretty simple and probably something you’d never have to write yourself.

The Real Magic

After looking at the LoadingState component, you’re probably wondering about those 2 magic utilities: createRenderProps and createStateSubscriber .

createRenderProps is essentially Redux, but it hides most of the API from you. If I wanted, I could switch it out for Redux or RxJS, and you wouldn’t even know.

createStateSubscriber is my one concession to the challenge. I had to use a stateful React component. This function acts like a higher-order component, but doesn’t wrap another component to provide it state, it is the component.

You call createStateSubscriber and it creates a component you can name and export. This removes a ton of boilerplate and makes it so your Redux-like state component doesn’t even need to know about the underlying view-model. It means you could be getting back a React, Vue, or WebComponent without having to change your state component at all!

Since the functionality behind those two functions isn’t really that important, I’ll let you explore them yourself instead:

Using Redux Reducers in React Components

Now for the mind-blowing masterpiece of Dan’s tweets. You can use Redux reducers in React components using createRenderProps and createStateSubscriber .

I’m going to reference payloadReducer from The Secret to Using Redux: createNamespaceReducer:

We’re going to turn this reducer into a state container:

Simple and straightforward. Again, you don’t need to worry about React, Redux, or anything else. Just create a generic component using generic reducers and actions, and you’ve got yourself a very very simple Redux-like state component.

React-based Stateful Container Components

What was Dan talking about in his original tweets? How would that look if we’re trying to do the same thing, but more to what he was saying? That’s what createStateSubscriber accomplishes.

My code looks a bit different than his, but here’s the Gist of both our implementations:

His version requires passing down a dispatch function whereas mine abstracts that out. Normally I’d be find passing a dispatch function like with ReduxConnection , but in this case, I wanted these state components to be completely separate from the underlying architecture. They already have the entire API inside so you should only be calling the actions they allow.

You could easily replicate auto-dispatched functions in his example as well by adding actions to the component class and calling dispatch from those components:

someAction(payload) {

this

.dispatch({

payload,

type: 'SOME_ACTION',

})

}

My solution is more complex because it’s doing a lot more such as abstracting away the underlying architecture and not even having a concept of React or even Redux. I originally wrote this code without Redux in mind and eventually came to using the same action-reducer-dispatch concept while working on it. The Flux concept simplified what I wanted to accomplish.

If I was in a pinch, I’d do it Dan’s way. No reason to write my own when React already handles it. If instead I need something independent of the underlying architecture, I’ll use what already wrote. The best thing about createStateSubscriber is it doesn’t really matter what’s happening under-the-hood. You can use whatever you want: Dan’s tweetable model or a more-complex Redux-like subscriber model or both. You should really build the solution that best fits your needs.

Try It, Use It, Break It

Try out Redux-like State Components yourself with this CodeSandbox: