蘑菇小姐会开花

redux

state&actions&reducer&subscriptions

  • state holds your js apps state.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {
    todos: [{
    text: 'sleeping',
    completed: true
    }, {
    text: 'reading',
    completed: false
    }],
    visibilityFilter: 'SHOW_COMPLETED'
    }
  • actions are called by your app.

    1
    2
    3
    4
    {
    type: 'ADD_TODO',
    text: 'take some rest'
    }
  • reducers handle the transformations between actions & store.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function todos(state = {}, action) {
    switch(action.type) {
    case 'ADD_TODO':
    return state.concat({
    text: action.text,
    completed: false
    })
    case 'TOGGLE_TODO':
    return state.map((todo, index) => action.index === index ? {...todo, completed: !todo.completed} : todo)
    default:
    return state
    }
    }
  • subscriptions listen for changes to the store.

三原则

  1. the single immutable state tree.
  2. describing state changes with actions.
    state 只读,需要通过dispatch actions 来改变state。
  3. a pure function reducer describe state mutations.
    接受prevState 和 action,返回nextState。

pure and impure functions

纯函数 输入决定输出,不会改变输入的内容

1
2
3
function sliceArr(x) {
return x.slice(1,2);
}

非纯函数

1
2
3
function spliceArr(x) {
return x.splice(1, 1)
}

Store

  1. holds the current application state object.
    getState()
  2. allows you to dispatch actions.
    dispatch()
  3. when you create it, you need to specify the reducer that tells how state is updated with actions.
    subscribe()

    createStore

    接受一个参数reducer
    getState 返回当前的state。
    subscribe 接受一个参数listener,返回一个unsubscribe方法。
    dispatch 接受一个action为参数,调用reducer方法,生成新的state。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    const createStore = reducer => {
    let state;
    let listeners = [];

    const getState = () => state

    const dispatch = (action) => {
    state = reducer(state, action);
    listeners.forEach(listener => listener())
    }

    const subsrcibe = (listener) => {
    listeners.push(listener)
    // 返回一个方法unsubscribe
    return () => {
    listeners = listeners.filter(l => l !== listener)
    }
    }

    // initial state
    dispatch({})

    return { getState, dispatch, subscribe}
    }

combineReducers

reducer composition calls existing reducers to manage their parts of the state, then combine the parts into a single state object.
接受reducers作为参数,返回一个reducer function。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// reducer composition
const todoApp = (state={}, action) => {
// todos/visibilityFilter reducer
return {
todos: todos(state.todos, action),
visibilityFilter: visibilityFilter(state.visibilityFilter, action)
}
}

/*
*combineReducers
*接受一个参数 reducers (object)
*返回一个reducer
*/
const combineReducers = reducers => {
// reducer 接受两个参数state,action,返回一个新的state
return (state = {}, action) => {
// reduce 累加器
return Object.keys(reducers).reduce((nextState, key) => {
nextState[key] = reducers[key](state[key], action)
return nextState
}, {})
}
}

const todoApp = combineReducers({ todos, visibilityFilter })

bindActionCreators

将action包装成直接可被调用的函数。

1
2
3
4
5
const bindActionCreators = (actionCreators, dispatch) => {
return Object.keys(actionCreators).reduce((total, key) => {
total[key] = (...args) => dispatch(actionCreators[key](...args))
}, {})
}

todo list app

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<!--redux store-->
const todo = (state = {}, action) => {
switch(action.type) {
case 'ADD_TODO':
return {
id: action.id,
text: action.text,
completed: false
};
case 'TOGGLE_TODO':
if(state.id !== action.id) {
return state;
}
return {
...state,
completed: !state.completed
};
default:
return state;
}
}

const todos = (state=[], action) => {
switch(action.type) {
case 'ADD_TODO':
return [...state,
todo(undefined, action)
];
case 'TOGGLE_TODO':
return state.map(t => todo(t, action))
default:
return state;
}
}

const visibilityFilter = (state='show all', action) => {
switch(action.type) {
case 'SET_VISIBLITY_FILTER':
return action.filter;
default:
return state;
}
}

const { combineReducers, createStore } = Redux;
const todoApp = combineReducers({ todos, visibilityFilter })
const store = createStore(todoApp)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
<!--react jsx-->
let nextTodoId = 0

const FilterLink = ({ filter, children, currentFilter }) => {
if(filter === currentFilter) {
return <span>{children}</span>
}
return <a
href= '#'
onClick={e => {
e.preventDefault();
store.dispatch({
type: 'SET_VISIBLITY_FILTER',
filter,
});
}}
>
{children}
</a>
}

const getVisibleTodos = (todos, filter) => {
switch(filter) {
case 'show_all':
return todos;
case 'show_active':
return todos.filter(t => !t.completed);
case 'show_completed':
return todos.filter(t => t.completed);
default:
return todos
}
}

class TodoApp extends Component {
render() {
const { todos, visibilityFilter} = this.props
const visibleTodos = getVisibleTodos(todos, visibilityFilter)
return (
<div>
<input ref={node => {
this.input = node
}}/>
<button onClick={() => {
store.dispatch({
type: 'ADD_TODO',
text: this.input.value,
id: nextTodoId ++
});
this.input.value = '';
}}>ADD TODO</button>
<ul>
{visibleTodos.map(todo => <li key={todo.id}
onClick={() => {
store.dispatch({
type: 'TOGGLE_TODO',
id: todo.id
});
}}
style={{
textDecoration: todo.completed ? 'line-through' : 'none'
}}
>
{todo.text}
</li>)}
</ul>
<p>
show:
<FilterLink filter='show_all' currentFilter={visibilityFilter}>All</FilterLink>
<FilterLink filter='show_active' currentFilter={visibilityFilter}>Active</FilterLink>
<FilterLink filter='show_completed' currentFilter={visibilityFilter}>Completed</FilterLink>
</p>
</div>
)
}
}

const render = () => {
ReactDOM.render(
<TodoApp
{...store.getState()}
/>,
document.getElementById('root')
)
}
store.subscribe(render)
render()
坚持原创技术分享,您的支持将鼓励我继续创作!