Files
talk/docs/frontend/REDUX.md
T
Belén Curcio ca9ef44202 links
2016-12-23 10:52:32 -03:00

224 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Redux
Redux is a predictable state container for JavaScript apps.
To understand Redux we need to dive into a few concepts.
- [Actions](#actions)
- [Action Creators](#actions)
- [Action Types](#actions)
- [Reducers](#reducers)
- [Stores](#store)
## The three principles
These are the three principles to build Redux applications. The following are specified in the Redux Documentation [Three Principles · Redux](http://redux.js.org/docs/introduction/ThreePrinciples.html)
### Single source of truth
The state of your whole application is stored in an object tree within a single store. We are going to represent the whole state of our application in a single Javascript Object.
### State is read-only
The only way to change the state is to emit an action, an object describing what happened.
### Changes are made with pure functions
To specify how the state tree is transformed by actions, you write pure reducers.
## Actions
Actions describe that something happened in our application. They are payloads of information that send data to your store. __They are the only source of information for the store.__
Here is an example:
```js
const ADD_COMMENT = 'ADD_COMMENT';
{
type: ADD_COMMENT,
comment: 'This is my comment.'
}
```
Actions are JavaScript objects. Every action must have a `type` property that indicates the type of action being performed. Types should be defined as constants.
Once an app becomes big enough, you may want to move them into a separate module. We store them in a `contants.js` file. [auth.js Constants](https://github.com/coralproject/talk/blob/153193959cb4dfa5d8feaabb49811325f836ee68/client/coral-framework/constants/auth.js)
```js
import { ADD_COMMENT, REMOVE_COMMENT } from './constants'
```
We can dispatch an action by using `dispatch()`.
Our actions live within the `coral-framework/actions` folder. [talk/client/coral-framework/actions](https://github.com/coralproject/talk/tree/153193959cb4dfa5d8feaabb49811325f836ee68/client/coral-framework/actions)
More about Actions: [Actions · Redux](http://redux.js.org/docs/basics/Actions.html)
### Async Actions
For our async operations we dispatch three actions.
- `<ACTION_TYPE>_REQUEST`
- `<ACTION_TYPE>_SUCCESS`
- `<ACTION_TYPE>_FAILURE`
#### Request
We use the postfix `_REQUEST` to know that the resource is being requested.
#### Success
We use the postfix `_SUCCESS` to know that the resource response came back successfully.
#### Failure
We use the postfix `_FAILURE` to know that the resource request failed.
## Action Creators
Action Creators are functions that return actions. This makes it easier to use, portable and testable.
```js
function addComment(comment) {
return {
type: ADD_COMMENT,
comment
}
}
```
So we can later trigger those actions by using `dispatch()`
```js
dispatch(addComment(comment))
dispatch(removeComment(comment.id))
```
## Dispatch Function
The `dispatch()` function can be accessed directly from the store as `store.dispatch()`, but more likely you'll access it using a helper like react-redux's`connect()`.
We use `connect()`in our containers. More about this in Architecture.
## Reducers
With Actions we describe that something happened in our application. But we dont specify how our state will be modified with this change.
In a Reducer we will specify how the state of our application change when an action has been dispatched.
Here we also will want to specify the `initialState`
Before building reducers its important to that you:
- Dont mutate the state
- Return the previous state in the default case.
Here is an example of an auth reducer:
```js
const initialState = {
isLoading: false,
loggedIn: false,
user: null,
error: ''
};
function auth (state = initialState, action) {
switch (action.type) {
case actions.CHECK_LOGIN_REQUEST:
return Object.assign({}, state, {
isLoading: true
});
case actions.CHECK_LOGIN_SUCCESS:
return Object.assign({}, state, {
isLoading: false,
loggedIn: true,
user: action.user,
error: ''
});
case actions.CHECK_LOGIN_FAILURE:
return Object.assign({}, state, {
isLoading: false,
error: action.error,
loggedIn: false,
user: null
});
default:
return state
}
}
```
Notice that a reducer takes the `state` as first argument and when its not defined it returns the `initialState`. As a second argument it takes the `action`. We have our state and we have the action. This is the time to specify how we modify the state.
### Reducers using ImmutableJS
We are using ImmutableJS to maintain our app state. Here is a guide on how to use ImmutableJS.
This is how a simplified version of our [auth reducer](https://github.com/coralproject/talk/blob/153193959cb4dfa5d8feaabb49811325f836ee68/client/coral-framework/reducers/auth.js) looks like:
```js
const initialState = Map({
isLoading: false,
loggedIn: false,
user: null,
error: ‘’
});
function auth (state = initialState, action) {
switch (action.type) {
case CHECK_LOGIN_REQUEST:
return state
.set('isLoading', true);
case CHECK_LOGIN_SUCCESS:
return state
.set('isLoading', false)
.set('loggedIn', true)
.set('user', action.user)
.set('error', '');
});
case CHECK_LOGIN_FAILURE:
return state
.set('isLoading', false)
.set('error', action.error)
.set('loggedIn', false)
.set('user', null)
});
default:
return state
}
}
```
Looks cleaner, right?
Its pretty easy to follow. Here it says if a `CHECK_LOGIN_REQUEST` action has been dispatched set the `isLoading` from our state to `true`. And we can show a tiny loader to let the user now we are requesting something to the server.
Our actions live within the `coral-framework/reducers` folder. [talk/client/coral-framework/reducers ](https://github.com/coralproject/talk/tree/153193959cb4dfa5d8feaabb49811325f836ee68/client/coral-framework/reducers)
More about Reducers: [Reducers · Redux](http://redux.js.org/docs/basics/Reducers.html)
And the last thing we need to see is the __Store__
### Store
The `Store` is what holds the application state. Here we can access and update the state.
Its important to note that we will only have a single store in our application called `rootReducer` and we will use reducer composition instead of many stores.
Here is an example of how create a store with [createStore()](http://redux.js.org/docs/api/createStore.html) using a reducer:
```js
import { createStore } from 'redux'
import authReducer from './auth'
let store = createStore(authReducer)
```
We do have a lot of stores so we will need to combine all our reducers with [combineReducers()](http://redux.js.org/docs/api/combineReducers.html) within a single store
```js
import {combineReducers} from 'redux';
import authReducer from './auth'
import configReducer from './config'
import userReducer from './user'
const rootReducer = combineReducers({
authReducer,
configReducer,
userReducer
...
});
```
More about Stores: [Store · Redux](http://redux.js.org/docs/basics/Store.html)
## Useful Resources
[Redux Documentation · Redux](http://redux.js.org/)
[Getting Started with Redux](https://egghead.io/courses/getting-started-with-redux)
[Usage with React · Redux](http://redux.js.org/docs/basics/UsageWithReact.html)