天天看點

redux應用筆記

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

redux應用筆記

簡介

redux是一個狀态管理工具,随着前端功能的增加,業務的複雜,将資料提取出元件是更好的方式,rudex是其中一種解決方案。

redux基本概念

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

store

store:倉庫,是一個包括項目所有的狀态資料的對象,redux提供了createStore函數來建立一個store

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