React進階(九):React-Redux
前言

React-Redux 將所有組件分成兩大類:UI 組件(presentational component)和容器組件(container component)。
UI組件
只負責 UI 的呈現,不帶有任何業務邏輯;
沒有狀態(即不使用this.state這個變量);
所有數據都由參數(this.props)提供;
不使用任何 Redux 的 API;
容器組件
負責管理數據和業務邏輯,不負責 UI 呈現;
帶有內部狀態;
使用 Redux 的 API;
UI 組件負責 UI 的呈現,容器組件負責管理數據和邏輯。
如果一個組件既有 UI 又有業務邏輯,那怎么辦?回答是,將它拆分成下面的結構:外面是一個容器組件,里面包了一個UI 組件。前者負責與外部的通信,將數據傳給后者,由后者渲染出視圖。
React-Redux 規定,所有的 UI 組件都由用戶提供,容器組件則是由 React-Redux 自動生成。也就是說,用戶負責視覺層,狀態管理則是全部交給它。
connect()
React-Redux 提供connect方法,用于從 UI 組件生成容器組件。connect的意思就是將這兩種組件連起來。
import { connect } from 'react-redux' const VisibleTodoList = connect()(TodoList);
1
2
上面VisibleTodoList 便是通過connect方法自動生成的容器組件。
但需要定義業務邏輯,組件才有意義。
import { connect } from 'react-redux' const VisibleTodoList = connect( mapStateToProps, mapDispatchToProps )(TodoList)
1
2
3
4
5
connect方法接受兩個參數:mapStateToProps和mapDispatchToProps。它們定義了 UI 組件的業務邏輯。前者負責輸入邏輯,即將state映射到 UI 組件的參數(props),后者負責輸出邏輯,即將用戶對 UI 組件的操作映射成 Action。
mapStateToProps()
它是一個函數,建立一個從(外部的)state對象到(UI 組件的)props對象的映射關系。
mapStateToProps執行后應該返回一個對象,里面的每一個鍵值對就是一個映射。
const mapStateToProps = (state) => { return { todos: getVisibleTodos(state.todos, state.visibilityFilter) } }
1
2
3
4
5
正如前面所說,mapStateToProps是一個函數,它接受state作為參數,返回一個對象。這個對象有一個todos屬性,代表 UI 組件的同名參數,后面的getVisibleTodos也是一個函數,可以從state算出 todos 的值。
mapDispatchToProps()
mapDispatchToProps是connect函數的第二個參數,用來建立 UI 組件的參數到store.dispatch方法的映射。它定義了哪些用戶的操作應該當作 Action,傳給 Store。它可以是一個函數,也可以是一個對象。
當mapDispatchToProps為函數時,則會得到dispatch和ownProps(容器組件的props對象)兩個參數。
const mapDispatchToProps = ( dispatch, ownProps ) => { return { onClick: () => { dispatch({ type: 'SET_VISIBILITY_FILTER', filter: ownProps.filter }); } }; }
1
2
3
4
5
6
7
8
9
10
11
12
13
從上面代碼可以看到,mapDispatchToProps作為函數,應該返回一個對象,該對象的每個鍵值對都是一個映射,定義了 UI 組件的參數怎樣發出 Action。
當mapDispatchToProps為對象時,它的每個鍵名也是對應 UI 組件的同名參數,鍵值應該是一個函數,會被當作 Action creator ,返回的 Action 會由 Redux 自動發出。
const mapDispatchToProps = { onClick: (filter) => { type: 'SET_VISIBILITY_FILTER', filter: filter }; }
1
2
3
4
5
6
connect方法生成容器組件以后,需要讓容器組件拿到state對象,才能生成 UI 組件的參數。
React-Redux 提供Provider組件,可以讓容器組件拿到state。
import { Provider } from 'react-redux' import { createStore } from 'redux' import todoApp from './reducers' import App from './components/App' let store = createStore(todoApp); render(
1
2
3
4
5
6
7
8
9
10
11
12
13
上面代碼中,Provider在根組件外面包了一層,這樣一來,App的所有子組件就默認都可以拿到state了。
Demo:簡單計數器
下面是一個計數器組件,它是一個純的 UI 組件。
class Counter extends Component { render() { const { value, onIncreaseClick } = this.props return (
1
2
3
4
5
6
7
8
9
10
11
上面代碼中,這個 UI 組件有兩個參數:value和onIncreaseClick。前者需要從state計算得到,后者需要向外發出 Action。
接著,定義value到state的映射,以及onIncreaseClick到dispatch的映射。
function mapStateToProps(state) { return { value: state.count } } function mapDispatchToProps(dispatch) { return { onIncreaseClick: () => dispatch(increaseAction) } } // Action Creator const increaseAction = { type: 'increase' }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
然后,使用connect方法生成容器組件。
const App = connect( mapStateToProps, mapDispatchToProps )(Counter)
1
2
3
4
然后,定義這個組件的 Reducer。
// Reducer function counter(state = { count: 0 }, action) { const count = state.count switch (action.type) { case 'increase': return { count: count + 1 } default: return state } }
1
2
3
4
5
6
7
8
9
10
最后,生成store對象,并使用Provider在根組件外面包一層。
import { loadState, saveState } from './localStorage'; const persistedState = loadState(); const store = createStore( todoApp, persistedState ); store.subscribe(throttle(() => { saveState({ todos: store.getState().todos, }) }, 1000)) ReactDOM.render(
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
完整代碼如下:
import React, { Component } from 'react' import PropTypes from 'prop-types' //類型檢查 import ReactDOM from 'react-dom' import { createStore } from 'redux' import { Provider, connect } from 'react-redux' // 定義counter組件 class Counter extends Component { render() { const { value, onIncreaseClick } = this.props // const value = this.props.value return (
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
拓展閱讀
React Redux文檔
React Redux 容器
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。