redux應用筆記

項目中應用到了redux,根據前人的代碼和網上百度到的資料總結如下,以免遺忘。

一圖勝千言

簡介

redux是一個狀態管理工具,隨着前端功能的增加,業務的複雜,將數據提取出組件是更好的方式,rudex是其中一種解決方案。

redux基本概念

redux由store、action、reducer 三部分組成。

store

store:倉庫,是一個包括項目所有的狀態數據的對象,redux提供了createStore函數來創建一個store

const store = createStore(reducer); //參數reducer下面介紹

action

action:動作,是一種包括類型和數據的對象,是reducer函數的參數,爲reducer提供參數來進行不同的處理,典型形式是:

  {
    type:"ADD_CART_NUM",
    data: data
  }

reducer

reducer:處理函數(我的翻譯),是一個具體的業務處理函數,位於action和disptch之間,被dispatch調用,根據傳入action對象數據進行處理並返回一個新的state。典型形式是:

// 通過判斷Action的類型,返回新的數據改變後的state對象,即使沒有任何狀態的改變,也要返回一個對象
// 注意:返回的state就是reducer提供的,也就是組件同步到的state
function counter(state = initState, action) { //state是reducer提供的默認值
  const count = state.count
  switch (action.type) {
    case ADD_CART_NUM:
      return { count: count + action.data.plusCount} //action.data是組件傳入的的值
    default:
      return state
  }
}

dispatch

dispath: 分發函數(我的翻譯),是store內部的函數,傳入action並調用reducer,一般形式爲:

<button onClick={()=>store.dispatch(myAction.increaseCartNum())}>Increase</button>
//其中increaseCartNum返回一個action對象,在dispath內部進行對應reducer的調用

subscribe

subscribe: 註冊監聽函數(我的翻譯),也是store內部的函數,用於組件同步store中數據,一般形式爲:

componentWillMount(){
  // 訂閱狀態變化
  store.subscribe((state)=>this.setState(state)) //註冊到監聽器函數到store中
}

問題 store中是如何區分不同組件的state呢?

redux流轉流程

假設store中數據位於上層,組件位於下層,那麼有數據上行和數據下行兩種流轉。

數據下行

store通過props傳遞到具體組件,組件通過this.props.xxx拿到數據。

數據上行

組件中點擊按鈕等操作調用store的dispatch函數(對應的action作爲函數參數),dispatch函數內部調用相應的recuder函數,改變並返回store數據

react-reduce

在react應用中,利用react-redux來對兩者進行橋接使用。
react提供了一個函數和一個組件,簡化redux流程
connect函數:實現同步store數據到組件的props中、暴露action函數隱藏對dispatch調用reducer。一般形式爲


// 綁定store中的狀態到組件的props
const mapStateToProps = state => {
	return {
		state
	}
}

//暴露action到組件props
const mapDispatchToProps = dispath => {
	return bindActionCreators({
		changeCartNum
	}, dispath)
}

export default connect(mapStateToProps, mapDispatchToProps)(MiniCart) //其中MiniCart是組件

Provider標籤用於將store傳遞到每個組件中,一般形式爲:

ReactDOM.render(
  <Provider store={store} >
      <Router>
          <LocaleProvider locale={zh_CN}><App /></LocaleProvider>
      </Router>
  </Provider >
  , document.getElementById('root'));

整體代碼

//action.js aciton文件
export function changeCartNum(data) {
    return {
        type: 'CART_NUM',
        data
    }
}


//frontReducer.js  子reducer文件,不同模塊分爲不同reducer文件

const init = { //reducer的初始狀態

}
 
const Front = (state = init, action = {}) => {
  switch (action.type) {
    case 'CART_NUM':
      //這裏可以添加一些處理函數,
      return { ...state, ...action.data }
  }
}
export default Front


//reducers.js 總reducers文件
import { combineReducers } from 'redux'

import Front from './FrontReducer';


const reducer = combineReducers({ //合併reducer文件
    Front,
    xxx,//不同的reducer文件
    xxx,//不同的reducer文件
})

export default reducer

//index.js文件

const store = createStore(reducer);
ReactDOM.render(
  <Provider store={store} > //傳遞store到內部組件中
      <Router>
          <LocaleProvider locale={zh_CN}><App /></LocaleProvider>
      </Router>
  </Provider >
  , document.getElementById('root'));

//B組件獲取cartNumber

import { changeCartNum } from '@actions';
class PublicTop extends React.Component {
  render(){
    ....
    <span >
      購物車數量
      {
        this.props.state.Front.cartNum 
      }
    </span>
    .....
  }
}

const mapStateToProps = state => {
	return {
		state
	}
}

const mapDispatchToProps = dispath => {
	return bindActionCreators({
		changeCartNum
	}, dispath)
}

export default connect(mapStateToProps, mapDispatchToProps)(MiniCart)


//A組件設置cartNumber

import { changeCartNum } from '@actions'; // 購物車數量
class Cart extends Component {
  render(){
    .......
      //增加購物車數量按鈕的點擊處理函數
      this.props.changeCartNum({ cartNum: totals })//totals是設置的值
    .......
  }
}

const mapStateToProps = state => {
	return {
		state
	}
}

const mapDispatchToProps = dispath => {
	return bindActionCreators({
		changeCartNum
	}, dispath)
}

export default connect(mapStateToProps, mapDispatchToProps)(Cart)


redux原生操作

以下程序是沒有使用react-redux的原生操作,包括store的簡易實現,用於理解redux的實現。

//reducer文件 counterReducer.js

// 提供一個初始的狀態
initState={
 count: 0 
}

// 通過判斷Action的類型,返回新的數據改變後的state對象,即使沒有任何狀態的改變,也要返回一個對象
export default function counter(state = initState, action) {
  const count = state.count
  switch (action.type) {
    case INCREMENT:
      return { count: count + 1 }
    default:
      return state
  }
}

//主文件 index.js

// 創建一個store全局管理state和操作
const store = createStore(reducer); 

// Provider在根組件<App>外面包了一層,App的所有子組件就默認都可以拿到store,通過組件的props傳遞
export default class Root extends Component {
    render() {
        return (
            <Provider store={store}>
                <App/>
            </Provider>
        )
    }
}

// createStore的簡單實現

function createStore = ( reducer ) => {
  let currentState; // 內部的狀態
  let listeners = []; //所有的監聽者
  
  const getState = () => currentState;  // 獲取store中的state
  
  // dispatch的操作就是內部執行reducer()函數,action和reducer在這兒產生交集,並且通知所有的監聽者
  const dispatch = ( action ) => {
    currentState = reducer(state, action); // 更新state
    listeners.forEach(listener => listener());
  }
  
  // 訂閱事件
  const subscribe = ( listener ) => {
    listeners.push(listener);
    return ()=>{
      listeners = listeners.filter(l => l !== listener)
    }
  }

  return {
    getState,
    dispatch,
    subscribe
  }
}



//組件文件
class Counter extends Component{

  componentWillMount(){
    // 訂閱狀態變化
    store.subscribe((state)=>this.setState(state)) //註冊到監聽器函數到store中
  }

  render() {
    return (
      <div>
        <span>{value}</span>
        //點擊後dispatch事件類型
        <button onClick={()=>store.dispatch(increaseAction.increase())}>Increase</button>
      </div>
    )
  }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章