From d928927da80c33513f5d4a4e7dc79ec23cefb0e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Fri, 10 May 2019 10:49:14 +0200 Subject: [PATCH] [DOC] store: add some basic documentation --- doc/component.md | 6 +- doc/store.md | 149 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 147 insertions(+), 8 deletions(-) diff --git a/doc/component.md b/doc/component.md index 19c2c99a..a5139797 100644 --- a/doc/component.md +++ b/doc/component.md @@ -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 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 @@ -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 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: - update `D`, @@ -428,6 +428,6 @@ Here is what Owl will do: 2. `willUnmount` hook on `E`, then destruction of `E`, 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` diff --git a/doc/store.md b/doc/store.md index a79ff36f..8b1ab806 100644 --- a/doc/store.md +++ b/doc/store.md @@ -1,8 +1,147 @@ # 🦉 Store 🦉 -Managing the state in an application is not an easy task. Many different -architectures/designs/systems/... have been created. We propose here to use -the idea of a central store. +## Content -- Store -- connect +- [Overview](#overview) +- [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 \ No newline at end of file