Redux: Colocating Selectors with Reducers

Dan Abramov
InstructorDan Abramov
Share this video with your friends

Social Share Links

Send Tweet
Published 9 years ago
Updated 6 years ago

We will learn how to encapsulate the knowledge about the state shape in the reducer files, so that the components don’t have to rely on it.

[00:00] My map state to props function uses the get visible todos function, and it passes the slice of the state corresponding to the todos. However, if I ever change the state structure, I'll have to remember to update this whole side.

[00:16] Alternatively, I can move the get visible todos function out of my view layer and place it in the file that knows best about the state todos internal structure.

[00:29] The file that determines the internal structure of todos is the file that contains the todos reducer. This is why I'm placing my get visible todos implementation right into the file with reducers, and I'm making it a named export.

[00:47] The convention I follow is simple. The default export is always the reducer function, but any named export starting with get is a function that prepares the data to be displayed by the UI. We usually call these functions selectors because they select something from the current state.

[01:06] In the reducers, the state argument corresponds to the state of this particular reducer. I'll follow the same convention in selectors, where the state argument will correspond to the state of the exported reducer in this file.

[01:21] Going back to my component, I still depend on the state structure because I read the todos from the state. The actual method of reading todos may change in the future.

[01:32] I am opening the file that contains the root reducer, and I add a named selector export there, as well. It is also called get visible todos, and it also accepts the state and the filter, but the state corresponds to the state of the combined reducer.

[01:51] Now I want to call the get visible todos function defined in the todos file alongside the reducer, but I can't use a named import because I have function with exactly the same name in the scope.

[02:04] This is why I'm using the name space import syntax that puts all the exports on an object, called from todos in this case, so I can use from todos.get visible todos to call this function I defined in the other file, and pass the slice of the state corresponding to the todos.

[02:27] Now I can go back to my component, and I can import get visible todos from the root reducer file. It encapsulates all the knowledge about the application state shape, so I can just pass it the whole state of my application, and it will figure out how to select the visible todos according to the logic described in selectors.

[02:51] Let's recap how I co-locate the selectors with the reducers. Inside map state to props, I call get visible todos, and I pass it the whole application state.

[03:03] The fresh value of the state will be passed anytime it changes to the map state to props function. I import get visible todos selector, and notice the curly brace. This is a named import from the file that defines the root reducer.

[03:18] In the reducer files, I started adding new methods with get prefix that are exported as named exports, as opposed to the reducer, which is exported as a default export. It's easy to tell a named export because it doesn't use the default keyword after the export keyword.

[03:38] I want to limit the knowledge about the exact state shape to the files contained in the reducers that manage this state. Todos is one of the reducers from which the root reducer is combined, so we know that its state is available as a state todos under the todos key.

[03:57] However, the state shape of the todos reducer should be encapsulated in the file where it's defined. This is why I'm delegating the further selection to the get visible todos I get from the from todos object that I import as a name space import from the todos file, so I get all the named exports from it.

[04:19] In the todos file, I define my reducer as the default export. But I also define the get visible todos implementation as a named export, and this time, the state refers to the state of just the corresponding reducer, which in this case is an array of todos.

[04:39] If I later want to change it to be something other than an array, I can change the get visible todos implementation. But I won't have to touch my components, because they don't rely on the state shape anymore.