You might also have reducers that combine one object with another, like this:

That’s the immutable version of a shallow merge, which we can replicate with Object.assign(store.config, config) .

I know I know, you’ve spent a large part of your life avoiding that first slot in Object.assign and it’s going to feel weird to stick stuff in there. But I promise you: it’s impossible to mutate the contents of the Recollect store, so have at it.

Adding to an array

You might have an action creator that dispatches an action that is handled by a reducer case with something like this objectively awful code:

“‘objectively” is the new “literally”

That’s the immutable version of todos.push() , so we would add something like this to the existing action creator:

Updating items in an array

You’ve got the point by now I’m sure — decode the immutable code to get the mutable version, and do that with the Recollect store.

But let’s do one more example, since immutability logic tends to be particularly gross when updating an item in an array.

This on its own is pretty bad, but let’s take a stroll through all the other spaghetti we might have in place to mark a todo as complete. I’ll understand if you glaze over as you read through this:

A click event in a <Todo> component fires.

component fires. <Todo> calls props.toggleTodo , with the props.index of the clicked todo — both props passed in from <TodoList> , which was passed toggleTodo from <TodoListContainer> .

calls , with the of the clicked todo — both props passed in from , which was passed from . <TodoListContainer> wrapped toggleTodo in a dispatch , so when it’s called …

wrapped in a , so when it’s called … toggleTodo dispatches a TOGGLE_TODO action with the index passed up from <Todo> (which was, ahem, passed down from <TodoList> )

dispatches a action with the passed up from (which was, ahem, passed down from ) A reducer case catches that TOGGLE_TODO and updates the todo, shallow cloning the todos array and the todo at the provided index .

The Recollect way is:

A click event in a <Todo> component fires.

component fires. <Todo> executes props.todo.completed = !props.todo.completed

You see, we already have a reference to the array item, so we can just update it. We don’t need to do this dance that relies on passing the index of the item in its array around through five different files.

But all this is just a tease — something to look forward to in the refactoring stage. For now, we’ll leave the spaghetti in place and update the todo in the action creator using the passed in index :

I was perhaps a tad bombastic earlier claiming that “no new code” was added. But as you will have noticed, so far we’re one-for-one replacing a line of code that dispatches an action describing what should be done, with a line of code that does it.

(Another way to think about it: you’re still dispatching an action, it’s just that the syntax to dispatch an update to the Recollect store happens to be the same JavaScript operators that you use to mutate an object. Internally these operations are intercepted and translated into an instruction to create the next version of the store.)

More complicated logic

If a reducer handling an action type has quite a lot of logic (and the replacement code is quite complicated), and that action type is dispatched from several places, you may want to wrap that logic up in an ‘updater’ function (which I’ll go into in a minute). For now, I’d suggest duplicating the code that writes to the Recollect store (gasp!) — pasting it next to each dispatch — and dropping a TODO to come back at the end to DRY it out (ungasp).

You might also have some code that needs to execute as a result of other changes to the store. For example if you want to set dirty: true whenever certain properties change, or sync some data to local storage, or update document.title if the page changes.

You can use Recollect’s afterChange function for this. It will execute a callback every time the store changes.

I’m generally not a fan of side effects like this, because they’re hard to track down, but they’re often the Least Flawed Option.

Making sure everything is in order

As you go along, you’ll want to make sure that the Recollect store is being populated as you expect. For this, it might be helpful to use Chrome’s live expressions in conjunction with __RR__.internals.store to show a live view of some part of the Recollect store in the dev tools.

A ‘live expression’

You can also scatter some console.assert statements around to compare the two stores.

Interlude: selectors and updaters

Recollect doesn’t have any rules about when and where you can interact with the store. You can import { store } from 'react-recollect' in any file. Or — as we’ll soon see — use the store prop of a collect ed component. It’s all the same store.

But just because your cool uncle state management library doesn’t impose any restrictions, it doesn’t mean your codebase should be the wild west. For the sake of organisation and shared terminology, you may find it useful to think in terms of:

Selectors : code that reads data from the store (similar to selectors and container components in Redux)

: code that reads data from the store (similar to selectors and container components in Redux) Updaters: code that updates the store (similar to Redux action creators and reducers)

I’ve documented some guidelines in the readme so I won’t go into any more detail here, but I’ll continue to use the terms ‘selector’ and ‘updater’ with these specific meanings.

End of interlude. On with the conversion …

Container components

You’ve got the Recollect store fully populated, shadowing your Redux store. Now it’s time to start reading from that store in your components.

This is exciting, right?!

I’m using the term ‘container component’, but to be specific: I’m referring to code that uses mapStateToProps and mapDispatchToProps with the react-redux connect function to provide data and action creators to a child component.

At this stage, we’ll have to break everything while we do a few things at once:

For any component currently ‘wrapped’ in a container component, wrap it in collect to get access to the Recollect store as a prop.

to get access to the Recollect store as a prop. Update your action creators so they can be called as normal functions

Import those functions into the components and call them directly

Delete the container components

It makes sense to go through these steps one container component at a time, but if you’ve got action creators used in multiple container components, you won’t be able to completely compartmentalise. If you want to convert one part of your app without disrupting the rest, you can duplicate any shared action creators.

Wrapping a component in collect

To start, wrap the child of the container component in Recollect’s collect function and change your component code to read its data from the new store prop. Just like with Redux, this function provides the data and updates the component when required.

If your container component was picking just the properties from the store that a component required, your component code would have been nice and focused:

With Recollect, you might wind up with some clutter as that logic is brought inside the component:

Even more so if you’re filtering/sorting/memoizing, etc.

Your options are probably fairly obvious:

inline the code as above

put it in a function at the top of the file — essentially a copy/paste of mapStateToProps .

. put it in a function in a different file (and call it a ‘selector’)

In fact, if you were really serious about the separation of ‘presentational’ and ‘container’ components, there’s nothing to stop you keeping a separate component just to provide data to another component. (Although I’d suggest now is a good time to question such architectural ‘best practices’).

Regardless of the path you take, for now I’d suggest minimising refactoring. Just copy the prop-selection code from your container component into the child component, drop a TODO, and come back to it at the end.

If you’re using prop types, I’d suggest adding a global StorePropType object that you can import and use in every component wrapped in collect . Since checking props counts as ‘reading’ from the store, Recollect would subscribe your component to everything in the store if we used prop-types . So you’ll want to switch out the prop-types library with the PropTypes equivalent exported by Recollect:

Sorry. I wish I could work out a better solution than this.

No other changes are required — under the hood it’s the same library. You can read more about it here.

If you’re using TypeScript, you can indicate that your component will receive the store as a prop by using the WithStoreProp type, described in Usage with TypeScript.

If you’re using Flow, cool, what’s it like in the past? Do people read those big newspapers on the train? Is the average surface temperature of earth a bit chilly? Look out, an asteroid!

Calling action creators directly

You’ll notice a subtle (pleasant) change in The New Way of doing things. With Redux, you can’t just call an action creator like a function. Redux needs to ‘know’ when you call this function so that it can handle the dispatched payload, send it to the reducers to create the next version of the store and update the appropriate components with it. In Recollect, all this plumbing is internal, so you can call your store-updating code like any other function.

For this step, go through each of the action creators that you’re passing down through your container component. In each one, delete any return statement (which would have been dispatched to reducers), and delete any explicit calls to dispatch .

If you’re using redux-thunk you can get rid of the function return. So an action creator that did look like this:

Will become this:

If you’re using redux-saga , you can do away with function* , yield , call , put , take , fork , spawn and whatever else you needed to fetch data from an API. Welcome to the async / await party, you’re going to like it here.