Skip to main content
Version: 4.0.0

Actions

Defining actions for providers

Data Provider does not concern about "actions", but normally, origins addons add its own methods for interacting with the data origin in different ways of "reading" the data, which is the main target of the library.

In this example we are going to see how to use the update, create and delete methods of the @data-provider/axios addon, and how to, again, isolate the actions from the views, so they don't have to use directly the provider methods.

Updating a todo

Let's add an action for updating todos. It will be a simple Function which will receive the id of the todo as first argument, and the new value of the completed property as second argument.

For updating a todo, we will use the todo provider, which corresponds to an specific todo "model". Querying it using the urlParams property will turn the url of the request into /todos/[id] (read the @data-provider/axios addon docs for further info about how to use its queries)

By default, the @data-provider/axios addon uses the PATCH HTTP verb when the update method is used, but this behavior can be changed using the provider options.

const updateTodo = (id, completed) => {
return todo.query({ urlParams: { id }}).update({
completed
});
};

Note the usage of the return statement in the actions. As provider methods return promises, we also return it in our function.

Deleting a todo

The action for deleting a todo is very similar to the one for updating it. It only differs in the provider method used. In this case, we will use the delete method, which by default uses the DELETE HTTP verb.

const deleteTodo = id => {
return todo.query({ urlParams: { id }}).delete();
};

Creating todos

In this case we are going to use the todos provider, as "creating" is not related to an specific model, but to the entire "collection". We are going to create todos uncompleted by default.

By default, the @data-provider/axios addon uses the POST HTTP verb when the create method is used.

const createTodo = text => {
return todos.create({
text,
completed: false
});
};

Cleaning caches

Now we have defined all actions for adding, modifying or deleting todos, but take into account that these actions are not modifying the state of the todos "collection". Data Provider is not concerned about modifying the local state for other actions than reading the data and handling the loading, loaded and error states.

The cache of each todo model is automatically cleaned after a successful update or delete method (this is made by the @data-provider/axios addon addon), which will result in requesting the data again to the server when its read method is called again. Also, the todos provider cache will be cleaned after a successful create method. But, todos is not being informed when a todo is updated or deleted. Let's add some code to manually clean the the cache of todos to our actions:

const updateTodo = (id, completed) => {
return todo.query({ urlParams: { id }}).update({
completed
}).then(response => {
todos.cleanCache(); // Clean "todos" cache when a todo is updated.
return Promise.resolve(response);
});
};
const deleteTodo = id => {
return todo.query({ urlParams: { id }})
.delete()
.then(response => {
todos.cleanCache(); // Clean "todos" cache when a todo is deleted.
return Promise.resolve(response);
});
};

There is no need to add anything to the "create" action, as todos cache is automatically cleaned when a new todo is added, and each independent todo is not affected when a new todo is added.

In our example we are going to use the @data-provider/react addon for UI bindings, so cleaning the cache will be enough to automatically refresh the views. The addon is listening to cleanCache events, and when the cache of a provider is cleaned, it will automatically "read" the data again, so data will be requested again to the server, and only if any component related to that data is "alive" in that moment. These means that you don't have to create a representation of the data from the server in client side and manually maintain it synchronized, each portion of the data simply will be retrieved again each time it is needed, and only when it is being visualized.

Another way of cleaning caches

We are cleaning the todos cache "manually" in our todo actions, but, what if our actions were not the unique method of updating or deleting a todo? Suppose one developer does not know about the existence of our actions, and uses directly the todo.update method from a view. Well, our todos collection will not be informed about a deletion, and that todo will be still visible in the todos list.

We can also use the Data Provider eventEmitter to declare relations between providers:

todo.onChild("*", eventName => {
if (eventName === "updateSuccess" || eventName === "deleteSuccess") {
todos.cleanCache();
}
});

The onChild method refers to listening events of any "children" (queried instances) of our provider (read the events API chapter for further info. The Axios addon emits updateSuccess and deleteSuccess errors when each method finish successfully. Then, we are cleaning the cache of todos every time a todo is updated or deleted, no matter if it was made using our actions or not.

In our example, as it is a very small project where things are very controlled, we are going to choose the first option, and we are going to manually clean the cache.

Source code

In a real project you'll probably prefer to separate your actions to different files from your providers and selectors, and that's right, but in this guide we are going to define them in the same file for simplicity.

We have also defined a cleanTodosCache function to avoid code duplication in updateTodo and deleteTodo actions.

data/todos.js

import { Axios } from "@data-provider/axios";
import { Selector } from "@data-provider/core";

export const todos = new Axios({
id: "todos",
url: "/todos"
});

export const todo = new Axios({
id: "todo",
url: "/todos/:id"
});

export const todosFiltered = new Selector(
todos,
(queryValue, todosResults) => {
if (queryValue.completed === null) {
return todosResults;
}
return todosResults.filter(todo => todo.completed === queryValue.completed)
},
{
id: "todos-filtered",
initialState: {
data: []
},
}
);

const cleanTodosCache = response => {
todos.cleanCache();
return Promise.resolve(response);
};

export const createTodo = text => {
return todos.create({
text,
completed: false
});
};

export const updateTodo = (id, completed) => {
return todo.query({ urlParams: { id }})
.update({ completed })
.then(cleanTodosCache);
};

export const deleteTodo = id => {
return todo.query({ urlParams: { id }})
.delete()
.then(cleanTodosCache);
};