在react項目中使用redux進行數據管理

前言

在我上一篇文章中,介紹瞭如何搭建一個簡單的react項目,裏面包含路由跳轉, 組件按需加載,及一部分我的理解上的項目文件構成,這篇文章將繼續爲你們 在上一篇文章的基礎上進行一個redux數據管理的功能實現及對代碼的分析研究.當前,整個redux構建流程只是基於我的對redux的研究理解及使用,若是有不一樣意見歡迎共同探討學習. 附上上一篇的連接 juejin.im/post/5d3ac0…css

關於redux

首先,在講操做以前,按照你們的慣例,先介紹一下redux,這裏我就直接複製粘貼官網連接了. www.redux.org.cn/java

操做前基礎步驟

首先依然是安裝依賴react

  • 安裝redux依賴, react-redux依賴, 以及 redux-thunk依賴
npm i  redux react-redux redux-thunk
複製代碼
  • 安裝針對數據操做的依賴
npm i immutable prop-types
複製代碼

安裝完依賴以後,就能夠在上一篇文章的基礎上,進行redux數據管理了.

首先依然是在src目錄下建立一個store文件夾,用於存放數據源文件, 而後在store目錄下建立一個store.js文件,將store變量暴露出來,給index.js使用.npm

這裏分爲三個步驟, 首先是建立文件夾與文件,而後先在index.js中添加用於添加數據管理的標籤Provider.

說明下Provider標籤的做用: Provider的做用redux

Provider是做爲整個App的容器,在你原有的App Container的基礎上再包上一層 接受Redux的store做爲props,並將其聲明爲context的屬性之一 子組件能夠在聲明瞭contextTypes以後能夠方便的經過this.context.store訪問到store.
附上一個連接 www.jianshu.com/p/186956ac6…數組

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import { Provider } from 'react-redux';
import store from './store/store';

ReactDOM.render(
    // Provider標籤用於聲明使用redux數據存儲包含的元素
    <Provider store={store}>
        <Router>
            <Route component= { App } />
        </Router>
    </Provider>
    , document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

複製代碼

第二步,咱們來把index.js中引入的store補全一下,

import { createStore, applyMiddleware, combineReducers } from 'redux'
import thunk from 'redux-thunk'

import * as pro from './Collecte/reducer'

let store =  createStore (
    combineReducers({...pro}),
    applyMiddleware(thunk)
)

export default store;
複製代碼

這裏說明一下, 從redux裏引入的三個部分可能比較不容易記, 做者提供一個簡單的方法,就是在store.js導入的時候,能夠先寫出從redux導入性能優化

import {} from 'redux'
複製代碼

而後,只須要記住, c c a ,三個部分的首拼,會自動提示出來, 這個小技巧頁適合任何其餘的文件導入.bash

接下咱們建立一個數據管理文件, 在store當中,每個頁面級組件的數據均可以建立一個文件夾存儲對應的數據操做文件.

上面咱們有這樣一句話,網絡

import * as pro from './Collecte/reducer'
複製代碼

這個的意思是從當前store目錄下的Collecte文件夾裏的reducer文件中導入全部暴露內容做爲pro對象.react-router

好了, 解釋完了咱們就來建立用於管理Collect頁面的數據文件. 這裏還要說明一下, 在使用redux管理數據時, 每一個頁面級組件數據文件夾下都建立三個文件, 一個action-type, action 以及reducer.

我的理解這也是redux的一種基本格式吧.下面針對三個文件,分別作下我的的理解 首先:

  • action-type.js文件 : 聲明操做數據類型,便於reducer.js中switch條件語句做邏輯判斷.

  • action.js 文件做用: 聲明獲取操做數據的方法, 各方法中設置方法調用的形參,與返回的action數據類型對象,

    action對象中應包含操做類型: 即在action-Type中聲明的操做類型; 若是是獲取數據源信息的方法 需包含dataList對象用於存儲獲取到的state初始數據. 若是是操做數據的方法, 則需包含便於在數據源信息中查詢到須要修改元素的標識,如index, id等等(具有惟一性) 簡而言之, 若是是操做數據的方法, 返回action對象中應包含操做數據類型及形參.

  • redux.js用於建立針對action數據變化所進行的一些邏輯操做,簡單來講就是用來提供改變數據的邏輯.

好了,理解了每一個文件的做用,那麼接下來,咱們來依次補全這三個文件.

首先是action-type.js, 這個文件夾,咱們須要聲明的是數據操做類型.

這裏,咱們聲明四個常量接收四個字符串, 都大寫是爲了防止出錯, 分別的意思就是英文翻譯: 獲取商品, 切換選中, 添加商品, 清除商品.

定好四個操做類型後,在redux.js中,當咱們對每一個操做類型定義業務邏輯的時候,就能夠用switch-case條件語句來對每一個類型進行添加業務邏輯了.

export const GETPRODUCTION = 'GETPRODUCTION'

export const TOGGLESELECT = 'TOGGLESELECT'

export const EDITPRODUCTION = 'EDITPRODUCTION'

export const CLEARSELECTED = 'CLEARSELECTED'
複製代碼

好了,補全了action-type文件,接下來咱們補全下action文件.

這裏總結幾個須要注意的地方:

  • 首先

  • 第二個,action文件定義操做數據的方法, 須要用到幾個方法,就定義幾個,每一個方法裏不須要寫邏輯,由於reducer文件會針對這些方法做業務邏輯,而後, 聲明方法中,須要定義業務邏輯須要使用的形參, 方法返回的是一個action集合,裏面包含判斷操做類型的type,和作業務邏輯須要用到的形參.

  • 第三個,以當前爲例,若是數據源是經過後臺請求獲得的,那麼就再getProData中使用async和await異步轉同步,將返回數據賦值給dataList.

API是一個統一的文件,存放當前項目所使用的業務網絡請求,下一篇會整理一些比較常規的項目結構以及代碼規範的問題.

import * as pro from './action-type'

// 當組件調用獲取數據方法時,返回記錄此時的數據信息做爲初始化數據類型.能夠在此處經過async await 
//異步轉同步方法請求後臺地址獲取參數,並將結果數據項做爲dataList參數值傳遞給state的dataList屬性.

export const getProData = () => {
    return {
        type: pro.GETPRODUCTION, //給返回的對象中添加type屬性的做用是在reducer文件中,能夠經過type屬性來判斷是什麼數據操做類型
        dataList: [ {selectNum: 1}, {selectNum: 2}, {selectNum: 3} ]
    }
}
//修改state數據屬性方法,聲明形參,並返回action對象集合, 在reducer文件中,
//經過獲取action對象來操做dataList修改存儲對應的值類型對象.
export const togSelectPro = index => {
    return {
        type: pro.TOGGLESELECT,
        index
    }
}

export const editPro = (index, selectNum) => {
    return {
        type: pro.EDITPRODUCTION,
        index,
        selectNum
    }
}
export const clearSelect = () => {
    return {
        type: pro.CLEARSELECTED
    }
}
複製代碼

好了,補全了action文件, 接下來咱們補全下用於處理業務邏輯的reducer文件

import * as pro from './action-type'
// 自測Immutable的方法意義, 相似後臺java語言方法.
import Immutable from 'immutable';

let defaultState = {
    dataList: []
}

// action: 對應調用action方法時,每一個方法返回的對象.
export const proData = (state = defaultState, action) => {
    let immuDataList; // 用於存儲操做數據源
    let immuItem; // 用於存儲當前須要操做的數據
    // 使用switch-case條件語句來進行業務邏輯區分
    switch (action.type) {
        // 若是操做類型是獲取Data
        case  pro.GETPRODUCTION:
            return {...state, ...action}
        //若是操做類型是切換選中
        case pro.TOGGLESELECT:
            //首先使用List方法 獲取到操做數據源
            immuDataList = Immutable.List(state.dataList)
            //而後使用Map方法,選擇出須要操做的元素
            immuItem = Immutable.Map(state.dataList[action.index])
            //而後使用set方法,改變選中元素的選中狀態, 用get方法獲取當前狀態再取反,達到切換效果
            immuItem = immuItem.set('selectStatus', !immuItem.get('selectStatus'))
            //而後把修改的數據寫入到數據源中,進行替換
            immuDataList = immuDataList.set(action.index, immuItem)
            // 完成後, 再將新的數據源返回出來
            return {...state, ...{dataList: immuDataList.toJS()}}
        // 若是操做類型是添加商品
        case pro.EDITPRODUCTION: 
            //一樣先使用List方法,獲取操做數據源
            immuDataList = Immutable.List(state.dataList)
            //而後使用Map方法,選擇出須要操做的元素
            immuItem = Immutable.Map(state.dataList[action.index])
            // 而後使用set方法,修改操做元素的值
            immuItem = immuItem.set('selectNum', action.selectNum)
             //而後把修改的數據寫入到數據源中,進行替換
             immuDataList = immuDataList.set(action.index, immuItem)
             // 完成後, 再將新的數據源返回出來
             return {...state, ...{dataList: immuDataList.toJS()}}
        //若是操做類型是清除商品
        case pro.CLEARSELECTED:
             // 獲取state數據中的數組集合
             immuDataList = Immutable.fromJS (state.dataList);
             //遍歷數組集合,經過set方法設置每一個元素的值 狀態
             for (let i = 0; i < state.dataList.length; i ++) {
                 immuDataList = immuDataList.update ( i, item => {
                     item = item.set('selectStatus', false);
                     item = item.set ('selectNum', 0);
                     return item;
                 })
             }
             //返回寫入數據的新集合對象
             return {...state, ...{dataList: immuDataList.toJS()}};
        default: 
             return state; 
    }
}
複製代碼

以上,咱們對於一個頁面級數據源的操做邏輯就設置完成了, 接下來就將它應用到頁面組件中.

這裏以Collecte頁面組件爲例. 首先須要建立鏈接, 這裏須要注意的是,一個文件中只能同時有一個exprot default

import React, { Component } from 'react';
import {Header} from '../../components'
// 組件與數據源創建連接
import { connect } from 'react-redux';
// 對數據進行深層判斷是否改變
import { is, fromJS } from 'immutable';
// prop-types用於設定數據類型,防止產生沒必要要的偏差,若是已有數據類型與設定數據類型不一致,則會提示報錯.
import PropTypes from 'prop-types';
import { getProData, togSelectPro, editPro } from '../../store/Collecte/action'

class Collecte extends Component {
    static propTypes = {
        // 設定proData必須是object集合類型
        proData: PropTypes.object.isRequired,
        // 設定如下三個屬性值必須是函數類型.
        getProData: PropTypes.func.isRequired,
        togSelectPro: PropTypes.func.isRequired,
        editPro: PropTypes.func.isRequired
    }
    shouldComponentUpdate (nextProps, nextState) {
        return !is(fromJS ( this.props), fromJS (nextProps)) || !is(fromJS (this.state), fromJS (nextState))
    }

    componentDidMount () {
        if (!this.props.proData.dataList.length) {
            let a = this.props.getProData();
            console.log(a)
        }
    }

    handleEdit = (index, num) => {
        let currentNum = this.props.proData.dataList[index].selectNum + num;
        if (currentNum < 0 ) {
            return
        }
        this.props.editPro (index, currentNum); 
    }
    render () {
        return (
            <div>
                <Header/>
                收集頁
                {
                    this.props.proData.dataList.map((item, index) => {
                        return <div className="pro-item-edit" key={index}>
                                <span onClick={this.handleEdit.bind(this, index, -1)}> - </span>
                                <span className="pro-num"> 當前數字爲: {item.selectNum} </span>
                                <span onClick={this.handleEdit.bind(this, index, 1)}> + </span>
                               </div>
                    })
                }
            </div>
        )
    }
}

// 創建鏈接,聲明數據信息及操做方法. 以及自調用對象(當前須要與數據源創建鏈接的類對象)
export default connect (state => (
    { proData: state.proData }
), {
    getProData,
    togSelectPro,
    editPro
})(Collecte)
複製代碼

當前頁面級組件與store創建鏈接,具體做用,我都寫在了註釋裏.那麼到這一步,在react中使用redux的步驟及各部分代碼的做用解析就完成了.

在當前窗口下,切換路由並不會影響collecte所改變的數據值,並且也能夠與其餘頁面共享數據.具體操做就簡單的複製粘貼一下,我就拿Detail這頁面級組件做爲觀察響應數據變化,

import React, { Component } from 'react';
import {Header} from '../../components';
import { connect } from 'react-redux';
// 對數據進行深層判斷是否改變
import { is, fromJS } from 'immutable';
// prop-types用於設定數據類型,防止產生沒必要要的偏差,若是已有數據類型與設定數據類型不一致,則會提示報錯.
import PropTypes from 'prop-types';
// 導入數據操做方法
import { getProData, togSelectPro, editPro } from '../../store/Collecte/action'

class Detail extends Component {
    static propTypes = {
        // 設定proData必須是object集合類型
        proData: PropTypes.object.isRequired,
        // 設定如下三個屬性值必須是函數類型.
        getProData: PropTypes.func.isRequired,
        togSelectPro: PropTypes.func.isRequired,
        editPro: PropTypes.func.isRequired
   }
     // 判斷數據是否改變, 若是相同返回false, 提高性能優化.
     shouldComponentUpdate (nextProps, nextState) {
        return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
    }

    componentDidMount () {
        if (!this.props.proData.dataList.length) {
            this.props.getProData()
        }
    }
    render () {
        return (
            <div>
                <Header/>
                詳情頁
                {
                    this.props.proData.dataList.map((item, index) => {
                        return <div className="pro-item-edit" key={index}>
                                <span className="pro-num"> 當前數字爲: {item.selectNum} </span>
                               </div>
                    })
                }
            </div>
        )
    }
}

export default connect(state => (
    {proData: state.proData}
), {
    getProData,
    togSelectPro,
    editPro
})(Detail)
複製代碼

這邊,加上與Collecte組件相同的返回hTML結構,能夠造成雙向響應, 但我這裏就簡單的填一下,看官們能夠本身實測一下.

好了,有關於在react項目中使用redux進行數據管理就講到這裏, 若是有不一樣的看法,歡迎共同探討學習.

相關文章
相關標籤/搜索