在我上一篇文章中,介紹瞭如何搭建一個簡單的react項目,裏面包含路由跳轉, 組件按需加載,及一部分我的理解上的項目文件構成,這篇文章將繼續爲你們 在上一篇文章的基礎上進行一個redux數據管理的功能實現及對代碼的分析研究.當前,整個redux構建流程只是基於我的對redux的研究理解及使用,若是有不一樣意見歡迎共同探討學習. 附上上一篇的連接 juejin.im/post/5d3ac0…css
首先,在講操做以前,按照你們的慣例,先介紹一下redux,這裏我就直接複製粘貼官網連接了. www.redux.org.cn/java
首先依然是安裝依賴react
npm i redux react-redux redux-thunk
複製代碼
npm i immutable prop-types
複製代碼
首先依然是在src目錄下建立一個store文件夾,用於存放數據源文件, 而後在store目錄下建立一個store.js文件,將store變量暴露出來,給index.js使用.npm
說明下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();
複製代碼
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
上面咱們有這樣一句話,網絡
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文件定義操做數據的方法, 須要用到幾個方法,就定義幾個,每一個方法裏不須要寫邏輯,由於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
}
}
複製代碼
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結構,能夠造成雙向響應, 但我這裏就簡單的填一下,看官們能夠本身實測一下.