Redux是什么:Redux是一個(gè)專門用來做狀態(tài)管理的JS庫(不是React插件庫)掏导。它可以用在React资盅,Angular晴股,Vue應(yīng)用中愿伴,但基本和React配合使用。用于集中式管理React應(yīng)用中多個(gè)組件共享的狀態(tài)电湘。
首先我們先介紹一個(gè)概念集中式管理公般,集中式管理要求狀態(tài)在哪里,修改狀態(tài)的行為就定義在那個(gè)組件胡桨。
但是集中式管理有一個(gè)問題。一個(gè)組件下面有很多路由組件瞬雹。如果這些組件的行為全部放在一個(gè)文件下會(huì)造成很大的冗余昧谊。
然而Redux使用集中式管理來管理狀態(tài)。將狀態(tài)以及狀態(tài)管理的方法放在一處酗捌,需要的時(shí)候調(diào)用狀態(tài)管理的方法呢诬。此時(shí)狀態(tài)不在組件的內(nèi)部,而是在Redux中胖缤。
組件的兩大功能:
- 展現(xiàn)數(shù)據(jù)
- 與用戶交互更新數(shù)據(jù)
Action Creators尚镰,Store,Reducers都是Redux代碼哪廓。因此整個(gè)過程可以看做是React組件和Redux進(jìn)行交互狗唉。
展現(xiàn)數(shù)據(jù):Store是Redux中的核心對(duì)象。從圖中可以看出React組件是從Store中讀取狀態(tài)涡真。
更新新的狀態(tài)顯示:使用分發(fā)功能時(shí)會(huì)使用到action分俯,其中有兩個(gè)屬性type,data哆料。Action Creators是一個(gè)工廠函數(shù)缸剪,用于修改action對(duì)象。我們需要傳遞一個(gè)type和data东亦。type代表的是事件的類型杏节。Action Creators通過type決定生成什么類型的Action。data是相關(guān)的數(shù)據(jù)典阵。
Reducers相當(dāng)于一個(gè)函數(shù)奋渔,該函數(shù)的形參是previousState和action,返回值為newState萄喳。返回的newState交給store進(jìn)行狀態(tài)的更新卒稳。
React中狀態(tài)無法直接更新,必須調(diào)用this.setState()他巨。
總結(jié):一個(gè)組件重要的功能是兩方面:顯示狀態(tài)和更新狀態(tài)充坑。顯示狀態(tài)可以通過Store來修改狀態(tài)减江。更新狀態(tài)時(shí)我們主要做兩件事,首先是發(fā)送通知捻爷,通過dispatch辈灼。之后是實(shí)現(xiàn)Reducers函數(shù)產(chǎn)生新的狀態(tài)。
什么情況下需要使用Redux
- 總體原則:能不用就不用也榄,如果不用比較吃力才考慮使用巡莹。
- 某個(gè)組件的狀態(tài),需要共享甜紫。
- 某個(gè)狀態(tài)需要在任何地方都可以拿到降宅。
- 一個(gè)組件需要改變?nèi)譅顟B(tài)。
- 一個(gè)組件需要改變另一個(gè)組件的狀態(tài)囚霸。
使用Redux
首先先下載依賴包
npm install --save redux
Store對(duì)象
作用:Redux庫最核心的管理對(duì)象腰根。
內(nèi)部維護(hù):state,reducer
核心方法:
- getState()
- dispatch(action)
- subscribe(listener)
編碼:
- store.getState()
- store.dispatch({type:'INCREMENT', number})
- store.subscribe(render)
Redux三個(gè)核心概念
action
標(biāo)識(shí)要執(zhí)行行為的對(duì)象
包含兩個(gè)方面的屬性:
- type:標(biāo)識(shí)屬性拓型,值為字符串额嘿,唯一,必要屬性劣挫。
- xxx:數(shù)據(jù)屬性册养,值為任意類型,可選屬性压固。
例子:
const action = {
type: "INCREMENT",
data: 2
}
Action.Creator(Action的工廠函數(shù))的創(chuàng)建
const increment = (number) => ({type: "INCREMENT", data: number})
reducer
根據(jù)老的state和action產(chǎn)生新的state的純函數(shù)球拦。
例子:
export default function counter(state=0, action){
switch(action.type){
case: "INCREMENT":
return state+action.data
case: "DECREMENT":
return state-action.data
default:
return state
}
}
注意:
- 每一次都返回一個(gè)新的狀態(tài)數(shù)據(jù)。
- 不要改變以前的狀態(tài)數(shù)據(jù)帐我。
store
將state, action與reducer聯(lián)系到一起的對(duì)象刘莹。
如何得到該對(duì)象:
import {createStore} from 'redux'
import reducer from './reducers'
const store = createStore(reducer)
此對(duì)象的功能:
getStete():得到state
dispatch(action):分發(fā)action,觸發(fā)reducer的調(diào)用焚刚,產(chǎn)生新的state点弯。
subscribe(listener):注冊(cè)監(jiān)聽,當(dāng)產(chǎn)生了新的state對(duì)象時(shí)矿咕,自動(dòng)調(diào)用抢肛。
Redux實(shí)例
我們需要實(shí)現(xiàn)以下的效果:
點(diǎn)擊“加號(hào)”,“減號(hào)”按鈕碳柱,上面的數(shù)字分別增加或減少顯示的數(shù)字捡絮。點(diǎn)擊“increment if odd”如果顯示的數(shù)字為奇數(shù)則增加顯示的數(shù)據(jù)奋隶。點(diǎn)擊“increment async”隔一段時(shí)間后增加顯示的數(shù)據(jù)接谨。
首先在index.js中引入Store對(duì)象
import {createStore} from 'redux'
之后是創(chuàng)建Store對(duì)象
const store = createStore()
之后是編寫Action Creators(reducers.js)并將其傳給Store對(duì)象撮抓。和前面說的一樣Action Creators相當(dāng)于一個(gè)工廠方法根據(jù)傳遞的type決定產(chǎn)生什么樣的Action糠雨。
export function counter(state = 0, action) {
switch (action.type){
case "INCREMENT":
return state + action.data
case "DECREMENT":
return state - action.data
default:
return state
}
}
這里做一些改進(jìn)。由于我們?cè)趥鬟ftype的時(shí)候可能會(huì)將type的類型寫錯(cuò)标沪。所以這里我們最好寫一個(gè)類(action-types.js)來統(tǒng)一管理這些字符贮配。
export const INCREMENT = 'INCREMENT'
export const DECREMENT = 'DECREMENT'
之后在reducers.js中引入之前寫的action-types.js
import {INCREMENT, DECREMENT} from './action-types'
export function counter(state = 0, action) {
switch (action.type){
case INCREMENT:
return state + action.data
case DECREMENT:
return state - action.data
default:
return state
}
}
這個(gè)時(shí)候我們就不需要擔(dān)心type寫錯(cuò)的問題了囊卜。
之后回到index.js中,引入之前寫的reducers.js越妈。并將Action Creators傳遞給Store對(duì)象季俩。并將Store對(duì)象傳遞給組件類。
import {counter} from './redux/reducers'
const store = createStore(counter)
function new_render(){
render(<App store={store}/>, document.getElementById('root'))
}
new_render()
store.subscribe(new_render)
讓我們來到組件類app.jsx梅掠。由于狀態(tài)是被Redux管理的酌住。因此在組件類中我們無需定義state了。狀態(tài)值的獲取與更新也要經(jīng)過Redux阎抒。不需要自己寫方法了酪我。例如:
//獲取狀態(tài)值
const count = this.props.store.getState()
//更新狀態(tài)值
this.props.store.dispatch({type: INCREMENT, data: number})
整個(gè)app.jsx的代碼如下:
import React, {Component} from 'react'
import {INCREMENT, DECREMENT} from '../redux/action-types'
class App extends Component{
increment = () => {
const number = this.select.value*1
this.props.store.dispatch({type: INCREMENT, data: number})
}
decrement = () => {
const number = this.select.value*1
this.props.store.dispatch({type: DECREMENT, data: number})
}
incrementIfOdd = () => {
const number = this.select.value*1
const count = this.props.store.getState()
if(count %2 === 1)
// this.setState({count: count + number})
this.props.store.dispatch({type: INCREMENT, data: number})
}
incrementAsync = () => {
const number = this.select.value*1
setTimeout(() => {
this.props.store.dispatch({type: INCREMENT, data: number})
}, 1000)
}
render(){
const count = this.props.store.getState()
return (
<div>
<p>Click {count} times</p>
<div>
<select ref={select => this.select = select}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>increment if odd</button>
<button onClick={this.incrementAsync}>increment async</button>
</div>
</div>
)
}
}
export default App
使用react-redux進(jìn)行簡(jiǎn)化
react-redux是一個(gè)react插件庫。專門用于簡(jiǎn)化在react應(yīng)用中使用redux且叁。
export default connect(
state => ({count: state}),
{increment, decrement}
)(App)
在此示例中祭示。state和{increment, decrement}會(huì)被結(jié)構(gòu)傳遞給App組件。狀態(tài)放到了Redux中谴古,我們需要這些狀態(tài)就需要屬性來接收。connect起到了傳遞屬性的作用稠歉。注意:connect中的屬性名必須和組件中的屬性名一致掰担。
React-Redux將所有的組件分為兩大類:
-
UI組件
不使用Redux的API
一般放在components文件夾下 -
容器組件
使用Redux的API
一般保存在containers文件夾下
action-types.js:包含所有action類型的名稱常量。
actions.js:包含了所有的action怒炸,工廠函數(shù)带饱。
reducers.js:包含多個(gè)reducer函數(shù)。根據(jù)老的state和action阅羹,返回一個(gè)新的state勺疼。
store.js:redux最核心的管理對(duì)象。
components下存放著UI組件(counter.jsx)
import React, {Component} from 'react'
import PropTypes from 'prop-types'
export default class Counter extends Component{
static propTypes = {
count: PropTypes.number.isRequired,
increment: PropTypes.func.isRequired,
decrement: PropTypes.func.isRequired
}
increment = () => {
const number = this.select.value*1
this.props.increment(number)
}
decrement = () => {
const number = this.select.value*1
this.props.decrement(number)
}
incrementIfOdd = () => {
const number = this.select.value*1
const {count} = this.props
if(count %2 === 1)
// this.setState({count: count + number})
this.props.increment(number)
}
incrementAsync = () => {
const number = this.select.value*1
setTimeout(() => {
this.props.increment(number)
}, 1000)
}
render(){
const {count} = this.props
return (
<div>
<p>Click {count} times</p>
<div>
<select ref={select => this.select = select}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>increment if odd</button>
<button onClick={this.incrementAsync}>increment async</button>
</div>
</div>
)
}
}
containers下放容器組件(app.jsx)
import {decrement, increment} from "../redux/actions";
import {connect} from "react-redux";
import Counter from "../components/counter";
export default connect(
state => ({count: state}),
{increment, decrement}
)(Counter)
Provider組件:
讓所有組件都可以得到state數(shù)據(jù)
<Provider store={store}>
<App />
</Provider>
connect組件:
用于包裝UI組件形成容器組件捏鱼。將組件和Redux關(guān)聯(lián)起來执庐。
import {connect} from 'react-redux'
connect(
mapStateToProps,
mapDispatchToProps
)(Counter)
mapStateToProps():
將外部數(shù)據(jù)(即state對(duì)象)轉(zhuǎn)換為UI組件的標(biāo)簽屬性。
const mapStateToProps = function(state){
return (
{value: state}
)
}
mapDispatchToProps():
將分發(fā)action的函數(shù)轉(zhuǎn)換為UI組件的標(biāo)簽屬性导梆。
簡(jiǎn)潔語法可以直接指定為actions對(duì)象或包含多個(gè)action方法的對(duì)象轨淌。
管理多個(gè)Reducer
import {combineReducers} from 'react'
合并多個(gè)reducer放在一起管理。
假設(shè)此時(shí)有兩個(gè)reducer看尼。我們不應(yīng)將它們分別暴露递鹉。而是統(tǒng)一放到combineReducers方法中進(jìn)行統(tǒng)一暴露。同時(shí)之前暴露的方法也要統(tǒng)一改成統(tǒng)一管理的方法藏斩。