Redux初探

Redux是什么:Redux是一個專門用來做狀態(tài)管理的JS庫(不是React插件庫)。它可以用在React,Angular,Vue應用中,但基本和React配合使用。用于集中式管理React應用中多個組件共享的狀態(tài)。

首先我們先介紹一個概念集中式管理,集中式管理要求狀態(tài)在哪里,修改狀態(tài)的行為就定義在那個組件。

但是集中式管理有一個問題。一個組件下面有很多路由組件。如果這些組件的行為全部放在一個文件下會造成很大的冗余。

然而Redux使用集中式管理來管理狀態(tài)。將狀態(tài)以及狀態(tài)管理的方法放在一處,需要的時候調(diào)用狀態(tài)管理的方法。此時狀態(tài)不在組件的內(nèi)部,而是在Redux中。



組件的兩大功能:

  • 展現(xiàn)數(shù)據(jù)
  • 與用戶交互更新數(shù)據(jù)

Action Creators,Store,Reducers都是Redux代碼。因此整個過程可以看做是React組件和Redux進行交互。

展現(xiàn)數(shù)據(jù):Store是Redux中的核心對象。從圖中可以看出React組件是從Store中讀取狀態(tài)。

更新新的狀態(tài)顯示:使用分發(fā)功能時會使用到action,其中有兩個屬性type,data。Action Creators是一個工廠函數(shù),用于修改action對象。我們需要傳遞一個type和data。type代表的是事件的類型。Action Creators通過type決定生成什么類型的Action。data是相關的數(shù)據(jù)。

Reducers相當于一個函數(shù),該函數(shù)的形參是previousState和action,返回值為newState。返回的newState交給store進行狀態(tài)的更新。

React中狀態(tài)無法直接更新,必須調(diào)用this.setState()。

總結(jié):一個組件重要的功能是兩方面:顯示狀態(tài)和更新狀態(tài)。顯示狀態(tài)可以通過Store來修改狀態(tài)。更新狀態(tài)時我們主要做兩件事,首先是發(fā)送通知,通過dispatch。之后是實現(xiàn)Reducers函數(shù)產(chǎn)生新的狀態(tài)。

什么情況下需要使用Redux

  1. 總體原則:能不用就不用,如果不用比較吃力才考慮使用。
  2. 某個組件的狀態(tài),需要共享。
  3. 某個狀態(tài)需要在任何地方都可以拿到。
  4. 一個組件需要改變?nèi)譅顟B(tài)。
  5. 一個組件需要改變另一個組件的狀態(tài)。

使用Redux

首先先下載依賴包

npm install --save redux

Store對象

作用:Redux庫最核心的管理對象。
內(nèi)部維護:state,reducer
核心方法:

  • getState()
  • dispatch(action)
  • subscribe(listener)

編碼:

  • store.getState()
  • store.dispatch({type:'INCREMENT', number})
  • store.subscribe(render)

Redux三個核心概念

action

標識要執(zhí)行行為的對象
包含兩個方面的屬性:

  • type:標識屬性,值為字符串,唯一,必要屬性。
  • 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
  }
}

注意:

  1. 每一次都返回一個新的狀態(tài)數(shù)據(jù)。
  2. 不要改變以前的狀態(tài)數(shù)據(jù)。

store

將state, action與reducer聯(lián)系到一起的對象。
如何得到該對象:

import {createStore} from 'redux'
import reducer from './reducers'
const store = createStore(reducer)

此對象的功能:
getStete():得到state
dispatch(action):分發(fā)action,觸發(fā)reducer的調(diào)用,產(chǎn)生新的state。
subscribe(listener):注冊監(jiān)聽,當產(chǎn)生了新的state對象時,自動調(diào)用。

Redux實例

我們需要實現(xiàn)以下的效果:



點擊“加號”,“減號”按鈕,上面的數(shù)字分別增加或減少顯示的數(shù)字。點擊“increment if odd”如果顯示的數(shù)字為奇數(shù)則增加顯示的數(shù)據(jù)。點擊“increment async”隔一段時間后增加顯示的數(shù)據(jù)。

首先在index.js中引入Store對象

import {createStore} from 'redux'

之后是創(chuàng)建Store對象

const store = createStore()

之后是編寫Action Creators(reducers.js)并將其傳給Store對象。和前面說的一樣Action Creators相當于一個工廠方法根據(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
    }
}

這里做一些改進。由于我們在傳遞type的時候可能會將type的類型寫錯。所以這里我們最好寫一個類(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
    }
}

這個時候我們就不需要擔心type寫錯的問題了。

之后回到index.js中,引入之前寫的reducers.js。并將Action Creators傳遞給Store對象。并將Store對象傳遞給組件類。

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})

整個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>&nbsp;
                    <button onClick={this.increment}>+</button>&nbsp;
                    <button onClick={this.decrement}>-</button>&nbsp;
                    <button onClick={this.incrementIfOdd}>increment if odd</button>&nbsp;
                    <button onClick={this.incrementAsync}>increment async</button>&nbsp;
                </div>
            </div>
        )
    }
}

export default App

使用react-redux進行簡化

react-redux是一個react插件庫。專門用于簡化在react應用中使用redux。

export default connect(
    state => ({count: state}),
    {increment, decrement}
)(App)

在此示例中。state和{increment, decrement}會被結(jié)構(gòu)傳遞給App組件。狀態(tài)放到了Redux中,我們需要這些狀態(tài)就需要屬性來接收。connect起到了傳遞屬性的作用。注意:connect中的屬性名必須和組件中的屬性名一致。

React-Redux將所有的組件分為兩大類:

  • UI組件
    不使用Redux的API
    一般放在components文件夾下
  • 容器組件
    使用Redux的API
    一般保存在containers文件夾下
文件結(jié)構(gòu)

action-types.js:包含所有action類型的名稱常量。
actions.js:包含了所有的action,工廠函數(shù)。
reducers.js:包含多個reducer函數(shù)。根據(jù)老的state和action,返回一個新的state。
store.js:redux最核心的管理對象。
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>&nbsp;
                    <button onClick={this.increment}>+</button>&nbsp;
                    <button onClick={this.decrement}>-</button>&nbsp;
                    <button onClick={this.incrementIfOdd}>increment if odd</button>&nbsp;
                    <button onClick={this.incrementAsync}>increment async</button>&nbsp;
                </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關聯(lián)起來。

import {connect} from 'react-redux'
connect(
  mapStateToProps,
  mapDispatchToProps
)(Counter)

mapStateToProps():
將外部數(shù)據(jù)(即state對象)轉(zhuǎn)換為UI組件的標簽屬性。

const mapStateToProps = function(state){
  return (
    {value: state}
  )
}

mapDispatchToProps():
將分發(fā)action的函數(shù)轉(zhuǎn)換為UI組件的標簽屬性。
簡潔語法可以直接指定為actions對象或包含多個action方法的對象。

管理多個Reducer

import {combineReducers} from 'react'

合并多個reducer放在一起管理。

假設此時有兩個reducer。我們不應將它們分別暴露。而是統(tǒng)一放到combineReducers方法中進行統(tǒng)一暴露。同時之前暴露的方法也要統(tǒng)一改成統(tǒng)一管理的方法。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容