React相關知識點:關於Redux

李赫feixuan發表於2019-05-05

為什麼用Redux

一些小型專案,只使用 React 完全夠用了,資料管理使用props、state即可,那什麼時候需要引入Redux呢? 當渲染一個元件的資料是通過props從父元件中獲取時,通常情況下是A –> B,但隨著業務複雜度的增加,有可能是這樣的:A –> B –> C –> D –> E,E需要的資料需要從A那裡通過props傳遞過來,以及對應的E –> A逆向傳遞callback。元件BCD是不需要這些資料的,但是又必須經由它們來傳遞,這樣不太友好,而且傳遞的props以及callback對BCD元件的複用也會造成影響。或者兄弟元件之間想要共享某些資料,也不是很方便傳遞、獲取等。諸如此類的情況,就有必要引入Redux`了。

注:其實 A --> B --> C --> D --> E 這種情況,React不使用props層層傳遞也是能拿到資料的,使用Context即可。

Redux 的三大基本原則

redux的核心概念就是store、action、reducer

約法三章

State 是隻讀的 react文件中說惟一改變 state 的方法就是觸發 action,奇怪的是 我們說State是隻讀的,但是為啥現在又說可以修改了呢,其實我們並不是直接修改state,action”是用來向 store 表示要修改 state 的載體。所以我們並沒有直接設定state的方法,使用了dispatch(action) 傳送 action 至 store 是唯一改變 state 的方式

store

  1. UI唯一資料來源;
  2. 維持應用的 state; 
  3. 提供 getState() 方法獲取 state; 
  4. 提供 dispatch(action) 方法更新 state; 
  5. 通過 subscribe(listener) 註冊監聽器; 通過 subscribe(listener) 返回的函式登出監聽器。


action

必須擁有type屬性,用來描述發生什麼,action通過reducer處理生成newState後才能夠更改store資訊。但是為了更好的語義程式設計,Redux通過語句store.dispatch(action)來更新store,reducer對action的處理在內部處理。

reducer

僅僅只是根據action.type處理一下需要更新的state

建立目錄

React相關知識點:關於Redux

建立Store模組 資料來源

//store/index.js

/*引入createStore 是用來建立一個資料來源物件儲存我們的資料的*/

import { createStore, applyMiddleware, compose } from 'redux';
import { createLogger } from 'redux-logger';
//引用資料來源
// store是引用的reducer
// action會觸發reducer
import allreducers from './reducers/index';
//資料處理後會返回給資料來源 才能取到最新的資料  在根元件index裡獲取


const logger = createLogger();
const store = createStore(
    allreducers,
    applyMiddleware(logger)
)
console.log(store)
function listerner () {
    store.getState();
}
store.subscribe(listerner);

export default store;複製程式碼

React相關知識點:關於Redux

傳入Store

Store儲存了整個應用的單一狀態樹,所有容器元件都需要從store中讀取,我們可以store作為屬性傳遞給每個元件,子元件通過props獲取,但是如果巢狀過深,寫起來會很麻煩。還好,react-redux提供一個叫provider的元件,他可以讓所有元件都可以訪問到store(他的實現原理實際上是利用了react的context功能),而不必顯示的一層層傳遞了。
import { Provider } from 'react-redux'
import store from './store';


ReactDOM.render(
   <Provider store={store}>
      <BrowserRouter>
         {routes }
      </BrowserRouter>
   </Provider>,
    document.getElementById('app')
);複製程式碼

建立Reducer 處理業務資料

//reducers/index.js

import * as Types from 'src/store/constants'
import { combineReducers } from 'redux';

const initState = {
    isLogin: "未登入",
    themecolor: "red",
    count: 0
}


const loginReducer = (state = initState.isLogin ,action) => {
    switch (action.type) {
        case Types.LOGIN_TYPE:
            return state = action.isLogin
        default:
            return state
    }
}

const themeReducer = (state = initState.themecolor ,action) => {
    switch (action.type) {
        case Types.THEME_TYPE:
            return state = action.themecolor
        default:
            return state
    }
}

const counterReducer = (state = initState.count, action) => {
    switch(action.type) {
        case Types.ADD_TYPE: return state + 1;
        default: return state;
    }
}

export default combineReducers({
    isLogin:loginReducer,
    themecolor:themeReducer,
    count:counterReducer
})複製程式碼

注意:combineReducers() 函式的作用是把多個 reducer 合併成一個最終的 reducer 函式。

常量統一儲存

//constants/index.js

/*常量統一儲存,便於管理*/
export const LOGIN_TYPE = "LOGIN_TYPE"
export const THEME_TYPE = "THEME_TYPE"
export const ADD_TYPE = "ADD_TYPE"複製程式碼

建立Action模組

actions/index.js

import * as Types from 'src/store/constants'
export const loginAction = function(isLogin) {
    return {
        type: Types.LOGIN_TYPE,
        isLogin
    }
}

export const themeAction = function(themecolor) {
    return {
        type: Types.THEME_TYPE,
        themecolor
    }
}

export const addAction = () => {
    return {
        type: Types.ADD_TYPE
    }
}複製程式碼

元件呼叫

import React, {PureComponent} from "react"
import store from 'src/store'
import { loginAction,themeAction,addAction } from 'src/store/actions'

const counterfn = () => {
    document.getElementById("counter").innerHTML = store.getState().count
}
store.subscribe(counterfn);
class NwdLogin extends React.Component {
    constructor(props){
        super(props)
    }
    componentDidMount() {
    }
    render () {
        var state = store.getState();
        return (
            <div>
                <div id="counter"></div>
                <div>
                    <button onClick={() => store.dispatch(addAction())}>add </button>
                </div>
            </div>
        )
    }
}
export default NwdLogin複製程式碼

React相關知識點:關於Redux

React相關知識點:關於Redux

注意:在不使用react-redux的情況下 必須使用store.subscribe(listener), store更新後回撥listener,回撥函式裡面可以呼叫store.getStore()來獲取更新後得state喲~

結合 react-redux使用

react-redux,redux和react的橋樑工具。 react-redux將組建分成了兩大類,UI組建component和容器元件container。 

UI元件:

  1. 只負責 UI 的呈現,
  2. 不帶有任何業務邏輯 沒有狀態(即不使用this.state這個變數) 
  3. 所有資料都由引數(this.props)提供 不
  4. 使用任何 Redux 的 AP

容器元件:

  1. 負責管理資料和業務邏輯,不負責 UI 的呈現 
  2. 帶有內部狀態 
  3. 使用 Redux 的 API

1)通過connect方法將React元件和Redux連線起來,react-redux 中引入了 connect

import React, {PureComponent} from "react"
import { connect } from 'react-redux'
import { loginAction,themeAction,addAction } from 'src/store/actions'
class NwdLogin extends React.Component {
    constructor(props){
        super(props)
    }
    componentDidMount() {
    }
    render () {
        console.log(this.props)
        return (
            <div>
                    {this.props.isLogin}
                    <button onClick={() => this.props.loginAction("已登入")}>設定登入狀態</button>
                    <button style={{backgroundColor:this.props.themecolor}} onClick={() => this.props.themeAction("#fff")}>設定themecolor狀態{this.props.themecolor}</button>
            </div>
        )
    }
}
const mapStateToProps = (state,ownProps) => {
    return {
        isLogin: state.isLogin,
        themecolor: state.themecolor
    }
}

const mapDispatchToProps = ((dispatch,ownPorps) => {
    return {
        loginAction: (isLogin) => dispatch({
            type: "LOGIN_TYPE",
            isLogin: isLogin
        }),
        themeAction: (themecolor) => dispatch({
            type: "THEME_TYPE",
            themecolor: themecolor
        })
    }
})

export default connect(mapStateToProps,mapDispatchToProps)(NwdLogin)複製程式碼


React相關知識點:關於Redux

React相關知識點:關於Redux

React相關知識點:關於Redux

1)connect()() 其實這兒是兩次函式呼叫。首先。connect() 返回了一個函式,然後我們立刻呼叫了這個函式並給它傳遞進第二個括號內的引數。第二個引數一般是一個 React 元件這是一種常見的“函數語言程式設計”的寫法。

2)connect() 的第一個引數是個返回了一個物件的函式 mapStateToProps,這個物件的屬性將會變成 React 元件中的“props”的屬性,你可以看到他們的值來自 store 的 state。這裡我把這第一個引數命名為“mapStateToProps”,如命名所示,是希望它來將全域性 states 轉化成本地 component 的 props。 mapStateToProps() 將會接收到一個攜帶著完整的 Redux store 的引數 store,然後抽出本地元件需要的 states 轉換成 props。

3)

如果不寫mapDispatchToProps,只能自己手動呼叫this.props.dispatch()

import React, {PureComponent} from "react"
import { connect } from 'react-redux'
import { loginAction,themeAction,addAction } from 'src/store/actions'
class NwdLogin extends React.Component {
    constructor(props){
        super(props)
    }
    componentDidMount() {
    }
    render () {
        console.log(this.props)
        return (
            <div>
                    {this.props.isLogin}
                    <button onClick={() => this.props.dispatch(loginAction("已登入"))}>設定登入狀態</button>
                    <button style={{backgroundColor:this.props.themecolor}} onClick={() => this.props.dispatch(themeAction("#fff"))}>設定themecolor狀態{this.props.themecolor}</button>
            </div>
        )
    }
}
const mapStateToProps = (state,ownProps) => {
    return {
        isLogin: state.isLogin,
        themecolor: state.themecolor
    }
}

/*const mapDispatchToProps = ((dispatch,ownPorps) => {
    return {
        loginAction: (isLogin) => dispatch({
            type: "LOGIN_TYPE",
            isLogin: isLogin
        }),
        themeAction: (themecolor) => dispatch({
            type: "THEME_TYPE",
            themecolor: themecolor
        })
    }
})*/

export default connect(mapStateToProps)(NwdLogin)複製程式碼

react-redux中connect的裝飾器用法@connect

import React, {PureComponent} from "react"
import { connect } from 'react-redux'
import { loginAction,themeAction,addAction } from 'src/store/actions'

@connect(
    state => {
        return {
            isLogin: state.isLogin,
            themecolor: state.themecolor
        }
    },
    {
        loginAction,
        themeAction,
        addAction
    }
)
class NwdLogin extends React.Component {
    constructor(props){
        super(props)
    }
    componentDidMount() {
    }
    render () {
        console.log(this.props)
        return (
            <div>
                    {this.props.isLogin}
                    <button onClick={() => this.props.loginAction("已登入")}>設定登入狀態</button>
                    <button style={{backgroundColor:this.props.themecolor}} onClick={() => this.props.themeAction("#fff")}>設定themecolor狀態{this.props.themecolor}</button>
            </div>
        )
    }
}
export default NwdLogin
複製程式碼

redux的bindActionCreators

bindActionCreators是redux的一個API,作用是將單個或多個ActionCreator轉化為dispatch(action)的函式集合形式。 開發者不用再手動dispatch(actionCreator(type)),而是可以直接呼叫方法。


import React, {PureComponent} from "react"
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { loginAction,themeAction,addAction } from 'src/store/actions'

@connect(
    state => {
        return {
            isLogin: state.isLogin,
            themecolor: state.themecolor
        }
    },
    dispatch => bindActionCreators({loginAction,themeAction,addAction},dispatch)
)
class NwdLogin extends React.Component {
    constructor(props){
        super(props)
    }
    componentDidMount() {
    }
    render () {
        console.log(this.props)
        return (
            <div>
                    {this.props.isLogin}
                    <button onClick={() => this.props.loginAction("已登入")}>設定登入狀態</button>
                    <button style={{backgroundColor:this.props.themecolor}} onClick={() => this.props.themeAction("#fff")}>設定themecolor狀態{this.props.themecolor}</button>
            </div>
        )
    }
}
export default NwdLogin

複製程式碼


相關文章