Selectors
Defining selectors
A Selector combines origins or other selectors (what we call "dependencies"), and returns a new result.
Selectors have the same interface than providers, so views don't need to know if they are using a provider or a selector. Selectors can also be queried, and you can use the query value for querying the selector dependencies, or in your selector
function.
Whenever a dependency cache is clean, the selector cache will also be clean, and will be recalculated when it is read again.
Defining dependencies for a Selector has a very powerful API. You could receive the results of one dependency and use it to query the next one, use functions to return different providers depending of the results of previous dependencies, read dependencies in parallel, etc. This example only shows the basics, please check the Selector api to have an idea of everything you can do using selectors dependencies.
This means that, every data you need in a selector can be defined using his dependencies. It will care of retrieving all needed data, no matter how many origins are implied, and then parse, combine or transform the results into the expected format.
For those using the selector, they don't need to know where the data come from, and will have to handle an unique loading
state, the one provided by the selector itself.
Getting uncompleted todos
Let's define a selector for filtering todos in base of their completed
status:
import { Selector } from "@data-provider/core";
export const uncompletedTodos = new Selector(
todos,
todosResults => todosResults.filter(todo => todo.completed === false)
);
That's all. Now, if we call to uncompletedTodos.read()
, todos will be fetched from the server and filtered in client side, then returned to us. No matter how many times do you call to the read method, the selector function will not be re-executed until the cache of the todos
provider is cleaned. (Or until the selector cache itself is cleaned. In that case, the selector function will be re-executed, but the todos will not be fetched from the server again, in case the cache of the provider has not been cleaned)
Source code
We will keep both providers and selectors in the same file for better comprehension of the example:
data/todos.js
import { Axios } from "@data-provider/axios";
import { Selector } from "@data-provider/core";
export const todos = new Axios("todos", {
url: "/todos"
});
export const todo = new Axios("todo", {
url: "/todos/:id"
});
export const uncompletedTodos = new Selector(
todos,
todosResults => todosResults.filter(todo => todo.completed === false)
);
Extra: Handling selectors errors
In the case of any of the dependencies throws an error, the flow of reading the rest of selector dependencies will be stopped (in case you have not defined a catch
for the dependency), and you can handle the error in the error
property of the selector state. Remember that the read
method of providers and selectors returns a promise, so you could also handle errors using an standard promise catch
method.