Redux
1、核心概念
Redux的主要目的,即集中且可預測的管理資料。集中管理指:將所有需要管理的資料放到一個store中進行管理。可預測指:資料的變化可以預測。為了實現這個功能,Redux將資料的操作全部交給了reducer來管理,而在action中定義資料處理的動作。這樣當你的資料處理動作action傳過來時,store便已經知道將要對資料進行的處理。在Redux中主要有三個內容,分別是store,action以及reducer。
- 1、store:連線reducer和state,並且為處理資料提供介面,
- 2、action:描述對資料處理的動作,是store 資料的唯一來源
- 3、reducer:接收 state 和 action,並返回新的 state 的函式。
三大原則
- 單一資料來源:整個應用的state被儲存在一棵object tree中,並且object tree只存在於唯一一個store中。
避免資料被修改,以及確定唯一的修改資料的方法就是訪問store。
- State 是隻讀的:唯一改變state的方法就是觸發action,action是一個用於描述已發生事件的普通物件。
對store外部來說,只能訪問state資料,而不能修改,確保state不被其他的方法修改,而所有的資料修改方法都被reducer集中化處理。
- 使用純函式來執行修改:為了描述 action 如何改變 state tree,你需要編寫 reducers。
action描述處理資料這件事,對資料的處理是在reducer內完成的,接收先前的state以及action,並返回新的state。
使用Redux的步驟
以counter計數器為例,
1、建立state物件
對應第一個原則,單一資料來源,在Redux中所有的物件都被儲存在一個單一的物件中,
在store.js檔案中先定義所需的物件,如下:
const state = {
counter:99,
}
複製程式碼
2、定義action
- 官網:Action是把資料從應用傳到store的有效載荷。它是store資料的唯一來源。一般來說你會通過store.dispatch()將action傳到store。
- 簡單來說就是要求store處理資料的訊號,對於不同的訊號reducer對資料進行不同的處理。
實現加減操作,定義如下:
//實現加的increment操作:
const increment ={
type = 'INCREMENT'
}
// 減操作的action
const decrement = {
type: 'DECREMENT'
}
複製程式碼
- action建立函式,就是建立action的方法,只是簡單的返回一個action。 如下,定義的increment()函式,返回的就是簡單的一個action,方法內的text可以定義每次修改的數值的多少。
//返回加操作的action
function increment(text) {
return {
type: 'INCREMENT',
text
}
}
// 減操作decrement的action
function decrement() {
return {
type: 'DECREMENT',
}
}
複製程式碼
3、定義reducer
- reducer的職責就是根據不同的action來處理資料。根據第三條規則,使用純函式來執行修改。
對於純函式必須遵守以下約束。
- 不得改寫傳入的引數
- 不能呼叫系統I/O的API(如DOM操作、http請求)
- 不能呼叫不純的方法(如Date.now()或者Math.random())。
對於計數器案例,定義的reducer如下,
function reducer(state = initState, action) {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + action.text }
case 'DECRMENT':
return { count: state.count - 1 }
default:
return state
}
}
複製程式碼
需要注意
- 不能修改state,
- 在default的情況下返回舊的state
當資料量多時需要對資料進行拆分處理,可以定義多個reducer,然後進行合併。
function reducer(state = state,action){
return{
//狀態:reducer(state.狀態,action),
todos:todos(state.todos,action),
visibility:visibility(state.todos,action)
}
}
複製程式碼
4、建立store,並使用
store提供了三個API介面,分別是
- getState:用於獲取狀態
- dispatch:用於派發action
- subscribe:當狀態發生變化時執行的回撥函式
//可在建立store時,也可在定義reducer時,初始化資料
const store = Redux.createStore(reducer,state)
複製程式碼
使用回撥函式,以及執行函式:
store.subscribe(() => {
console.log(`現在的狀態為:${store.getState().count}`)
})
store.dispatch(increment(1))
store.dispatch(increment(3))
store.dispatch(decrment())
複製程式碼
react-redux
- react-redux是連結react與redux的專用庫。在使用時需要格外安裝。學習react-redux最主要的就是學習其中的三個一,一種開發思想,一個connect方法,以及一個provide元件。
- 1、一種開發思想:元件分離思想
react-redux提出了“智慧”元件和“笨拙”元件相分離的開發思想,
- “智慧”元件,即容器元件,主要特點有:(1)負責管理資料和業務邏輯,不負責UI的呈現,(2)帶有內部狀態,(3)使用redux的API。
- “笨拙”元件,又稱展示元件,特點:(1)只負責UI的呈現, 不帶有任何的業務邏輯,(2)沒有狀態,(3)所有資料由引數(this.props)提供,(4)不適用redux的API。
- 2、一個connect方法:將“笨拙”元件,變成“智慧”元件。
react-redux提供了一個connect方法,用於從“笨拙”元件生成容器元件。 其中狀態,是指state資料,邏輯,是指修改state的方法.
- 3、一個provider元件:為後代元件提供store物件。 在使用connect方法時,用到了store物件的dispatch方法,那麼此時我們就需要使用provide元件,將store傳遞過去了。
還是以counter計數器為例,但是這次使用腳手架create-react-app的方式:
//安裝腳手架create-react-app:
npm i create-react-app -g
//建立專案
create-react-app counter
//安裝react-redux
npm i redux react-redux
//然後啟動專案
npm start
複製程式碼
counter案例:
1、先定義元件
//定義Show元件
export default class Show extends React.Component{
render() {
return (<h2>counter:{this.props.counter}</h2>)
}
}
//定義Add元件,Sub元件與Add元件一樣
export default class Add extends Component{
render() {
return (<button>+</button>)
}
}
//定義counter元件,顯示內容
class Counter extends Component {
render() {
return (
<div>
<Show/>
<p><Add/> <Sub/> </p>
</div>
)
}
}
複製程式碼
2、建立store
- 1、定義store以及action creators
//定義store
const state = {counter:6,}
//定義increment的action,定義decrement的action與其類似。
export function increment() {
return {type: 'INCREMENT'}
}
複製程式碼
- 2、定義reducer以及合併
export default function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1
default:
return state
}
}
//合併reducer
const reducer = combineReducers({
counter// 當名字相同時,可以省略
})
複製程式碼
- 3、建立store
//可以在建立store時初始化資料
const store = createStore(reducer,state,)
//在使用框架,在不同的資料夾下寫模組時,要記得匯出以及匯入你需要的資料。
export default store
複製程式碼
- 4、使用connect轉化元件 在使用connect時,需要先匯入必要的方法
import { connect } from 'react-redux'
//匯入actions內的所有方法,並命名為action
import * as actions from '../actions/films'
import { bindActionCreators } from 'redux';
複製程式碼
在匯入必要的方法後,才能使用。
// 定義mapStarteTopProps,建立從state到props物件的對映,接受state作為引數
function mapStarteTopProps(state) {
return {counter: state.counter}}
// 定義mapDispatchToProps,用於建立笨拙元件的引數到store.dispatch方法的對映,以dispatch為引數
function mapDispatchToProps(dispatch) {
return bindActionCreators(action, dispatch)}
// 將Counter變成容器元件並匯出,需要在頂層使用 provide元件,引入store,這樣在子元件內就可以使用store了。
export default connect(mapStarteTopProps, mapDispatchToProps)(Counter)
複製程式碼
- 5、使用provider元件,傳遞store
//需要匯入必要的包
import { Provider } from 'react-redux';
import store from './store'
import Counter from './containers/Counter';
//再使用provider方法
ReactDOM.render(
// 將store整合到react應用的頂層props裡面,然後使用provider元件傳遞。這樣各個子元件就能訪問到頂層的store物件了
<Provider store={store}><Counter /></Provider>,
document.getElementById('root'));
registerServiceWorker();
複製程式碼
- 6、在各個子元件使用方法和屬性
部分修改如下:
//在Add元件內使用increment方法
export default class Add extends Component{
render() {
return (<button onClick={()=>this.props.increment()}>+</button>)
}
}
//修改Counter元件,顯示資料,以及給子元件傳遞方法
class Counter extends Component {
render() {
return (
<div>
<Show counter={this.props.counter} />
<p><Add increment={this.props.increment} />
<Sub decrement={this.props.decrement} />
</p>
</div>
)
}
}
複製程式碼
- 測試如下:
在react-redux中實現非同步操作
- 定義非同步的action creator函式
在非同步action中,返回一個函式,在該函式中去呼叫同步的action即可。 這個函式,會自動的獲取dispatch和getState。
//定義非同步操作,在點選後兩秒實現加操作
export function incrementAsync() {
return function (dispatch, getState) {
setTimeout(function () {
dispatch(increment());
}, 2000)
}
}
複製程式碼
- 先前已經定義好了框架,現在只需在定義好的框架內,定義方法以及呼叫方法即可。
//在Counter內傳遞方法
<AsyncAdd incrementAsync={this.props.incrementAsync}
//在Async元件內使用方法
export default class AsyncAdd extends Component {
render() {
return (<button onClick={() => this.props.incrementAsync()}>+async</button>)
}
}
複製程式碼
- redux預設是不支援非同步action的,因此我們需要使用中介軟體來強化store,讓Reducer 在非同步操作結束後自動執行。
//安裝實現非同步操作的中介軟體redux-thunk
npm i redux-thunk -S
複製程式碼
- 使用中介軟體:
//先從redux-thunk內匯入需要的方法
import thunkMiddleware from "redux-thunk"
// 在createStore方法上使用使用thunkMiddleware方法強化store
const createStoreWithMiddleware = applyMiddleware(
thunkMiddleware
)(createStore)
const store = createStoreWithMiddleware(reducer,state,)
複製程式碼
這樣就可以在redux內實現非同步操作了。