mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[DOC] store: add some basic documentation
This commit is contained in:
+3
-3
@@ -351,7 +351,7 @@ We give here an informal description of the way components are created/updated
|
|||||||
in an application. Here, ordered lists describe actions that are executed
|
in an application. Here, ordered lists describe actions that are executed
|
||||||
sequentially, bullet lists describe actions that are executed in parallel.
|
sequentially, bullet lists describe actions that are executed in parallel.
|
||||||
|
|
||||||
**Scenario 1: Initial Mounting** Imagine we want to render the following component tree:
|
**Scenario 1: initial rendering** Imagine we want to render the following component tree:
|
||||||
|
|
||||||
```
|
```
|
||||||
A
|
A
|
||||||
@@ -391,7 +391,7 @@ component (with some code like `app.mount(document.body)`).
|
|||||||
5. The method `mounted` is called recursively on all widgets in the following
|
5. The method `mounted` is called recursively on all widgets in the following
|
||||||
order: `B`, `D`, `E`, `C`, `A`.
|
order: `B`, `D`, `E`, `C`, `A`.
|
||||||
|
|
||||||
**Scenario 2: state change, rerendering**. Now, let's assume that the user clicked on some
|
**Scenario 2: rerendering a component**. Now, let's assume that the user clicked on some
|
||||||
button in `C`, and this results in a state update, which is supposed to:
|
button in `C`, and this results in a state update, which is supposed to:
|
||||||
|
|
||||||
- update `D`,
|
- update `D`,
|
||||||
@@ -428,6 +428,6 @@ Here is what Owl will do:
|
|||||||
2. `willUnmount` hook on `E`, then destruction of `E`,
|
2. `willUnmount` hook on `E`, then destruction of `E`,
|
||||||
3. (initial) patching of `F`, then hook `mounted` is called on `F`
|
3. (initial) patching of `F`, then hook `mounted` is called on `F`
|
||||||
|
|
||||||
5. patching of `D`,
|
5. patching of `D`
|
||||||
|
|
||||||
6. `patched` hooks are called on `D`, `C`
|
6. `patched` hooks are called on `D`, `C`
|
||||||
|
|||||||
+144
-5
@@ -1,8 +1,147 @@
|
|||||||
# 🦉 Store 🦉
|
# 🦉 Store 🦉
|
||||||
|
|
||||||
Managing the state in an application is not an easy task. Many different
|
## Content
|
||||||
architectures/designs/systems/... have been created. We propose here to use
|
|
||||||
the idea of a central store.
|
|
||||||
|
|
||||||
- Store
|
- [Overview](#overview)
|
||||||
- connect
|
- [Example](#example)
|
||||||
|
- [Reference](#reference)
|
||||||
|
- [Public API](#public-api)
|
||||||
|
- [Mutations](#mutations)
|
||||||
|
- [Actions](#actions)
|
||||||
|
- [Getters](#getters)
|
||||||
|
- [Connecting a component](#connecting-a-component)
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Managing the state in an application is not an easy task. In some cases, the
|
||||||
|
state of an application can be part of the component tree, in a natural way.
|
||||||
|
However, there are situations where some part of the state need to be displayed
|
||||||
|
in various parts of the user interface, and then, it is not obvious which
|
||||||
|
component should own which part of the state.
|
||||||
|
|
||||||
|
Owl's solution to this issue is a centralized store. It is a class that owns
|
||||||
|
some state, and let the developer update it in a structured way (through
|
||||||
|
mutations and actions). Owl components can then connect to the store, and will
|
||||||
|
be updated if necessary.
|
||||||
|
|
||||||
|
Note: Owl's store is inspired by React Redux and VueX.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Here is what a simple store look like:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const actions = {
|
||||||
|
addTodo({commit}, message) {
|
||||||
|
commit('addTodo', message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const mutations = {
|
||||||
|
addTodo({state}, message) {
|
||||||
|
const todo = {
|
||||||
|
id: state.nextId++,
|
||||||
|
message,
|
||||||
|
isCompleted: false,
|
||||||
|
};
|
||||||
|
state.todos.push(todo);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
todos: [],
|
||||||
|
nextId: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
const store = new owl.Store({state, actions, mutations});
|
||||||
|
store.on('update', () => console.log(store.state));
|
||||||
|
|
||||||
|
// updating the state
|
||||||
|
store.dispatch('addTodo', 'fix all bugs');
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Reference
|
||||||
|
|
||||||
|
The store is a simple `owl.EventBus` that triggers `update` events whenever its
|
||||||
|
state is changed. Note that these events are triggered only after a microtask
|
||||||
|
tick, so only one event will be triggered for any number of state changes in a
|
||||||
|
call stack.
|
||||||
|
|
||||||
|
Also, it is important to mention that the state is observed (with a `owl.Observer`),
|
||||||
|
which is the reason why it is able to know if it was changed. This implies that
|
||||||
|
state changes need to be done carefully in some cases (adding a new key to an
|
||||||
|
object, or modifying an array with the `arr[i] = newValue` syntax). See the
|
||||||
|
[Observer](observer.md)'s documentation for more details.
|
||||||
|
|
||||||
|
### Public API
|
||||||
|
|
||||||
|
1. `constructor`
|
||||||
|
2. `commit`
|
||||||
|
3. `dispatch`
|
||||||
|
|
||||||
|
### Mutations
|
||||||
|
|
||||||
|
Mutations are the only way to modify the state. Changing the state outside a
|
||||||
|
mutation is not allowed (and should throw an error). Mutations as synchronous.
|
||||||
|
|
||||||
|
### Actions
|
||||||
|
|
||||||
|
Actions are used to coordinate state changes. It is also useful whenever some
|
||||||
|
asynchronous logic is necessary. For example, fetching data should be done
|
||||||
|
in an action.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const actions = {
|
||||||
|
login({commit}) {
|
||||||
|
commit('setLoginState', 'pending');
|
||||||
|
try {
|
||||||
|
const loginInfo = await doSomeRPC('/login/', 'someinfo');
|
||||||
|
commit('setLoginState', loginInfo);
|
||||||
|
} catch {
|
||||||
|
commit('setLoginState', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Getters
|
||||||
|
|
||||||
|
Usually, data contained in the store will be stored in a normalized way. For
|
||||||
|
example,
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
posts: [{id: 11, authorId: 4, content: 'Greetings'}],
|
||||||
|
authors: [{id: 4, name: 'John'}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
However, the user interface will probably need some denormalized data like
|
||||||
|
|
||||||
|
```js
|
||||||
|
{id: 11, author: {id: 4, name: 'John'}, content: 'Greetings'}
|
||||||
|
```
|
||||||
|
|
||||||
|
This is what `getters` are for: they give a centralized way to process and
|
||||||
|
transform the data contained in the store.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const getters = {
|
||||||
|
getPost({state}) {
|
||||||
|
return function (id) {
|
||||||
|
const post = state.posts.find(p => p.id === id);
|
||||||
|
const author = state.authors.find(a => a.id = post.id);
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
author,
|
||||||
|
content: post.content
|
||||||
|
};
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Connecting a component
|
||||||
|
|
||||||
|
Todo
|
||||||
Reference in New Issue
Block a user