Redux使用學習筆記

Redux使用

基本概念

store

store 是應用狀態 state 的管理者,包含下列四個函數:
一、getState() 獲取整個 state
二、dispatch(action) 觸發 state 改變的【惟一途徑】
三、subscribe(listener) 訂閱state改變
四、replaceReducer(nextReducer)javascript

state

如何初始化?前端

複製代碼

如何獲取statevue

var state = store.getState()
複製代碼

action

作什麼用的?
是一個對象,裏面包含type字段和payload,type是給Reducer修改state的時候判斷用的,payload是要修改的數據java

{
  type: 'ADD_TODO',
  payload: {
    id: 1,
    content: '待辦事項1',
    completed: false
  }
}
複製代碼

action creator

負責複用action對象的,能夠給payload賦值不一樣數據react

function addTodo(content) {
  return {
    type: 'ADD_TODO',
    payload: {
      id: id++,
      content: content, // 待辦事項內容
      completed: false  // 是否完成的標識
    }
  }
}
複製代碼

action是能夠異步的,如何異步呢?git

export const INCREMENT_COUNTER = 'INCREMENT_COUNTER';
export const DECREMENT_COUNTER = 'DECREMENT_COUNTER';

export function increment() {
  return {
    type: INCREMENT_COUNTER
  };
}

export function decrement() {
  return {
    type: DECREMENT_COUNTER
  };
}

//這裏正常來講若是不給redux添加中間件的話這樣會報錯,可是引入redux-thunk這個中間件,就能夠改變原來的dispatch處理方式,支持返回函數而且進行異步處理,而且給返回的函數注入dispatch, getState
export function incrementIfOdd() {
  return (dispatch, getState) => {
    const { counter } = getState();
    if (counter % 2 === 0) {
      return;
    }

    dispatch(increment());
  };
}

export function incrementAsync(delay = 1000) {
  return (dispatch) => {
    setTimeout(() => {
      dispatch(increment());
    }, delay);
  };
}

複製代碼

那麼redux-thunk內部又是如何實現的呢?如下是源碼分析:github

//redux添加中間件源碼
export default function applyMiddleware(...middlewares) {
  return createStore => (...args) => {
    const store = createStore(...args)
    let dispatch = () => {
      throw new Error(
        `Dispatching while constructing your middleware is not allowed. ` +
          `Other middleware would not be applied to this dispatch.`
      )
    }

    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }
    //這裏負責將getState和dispatch注入
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}
return enhancer(createStore)(reducer, preloadedState)

//redux-thunk源碼

//傳入一些額外的
function createThunkMiddleware(extraArgument) {
// 這段寫法的意思是: 至關於函數柯里化將多個參數層層包裝
// return function ({
// dispatch,
// getState
// }) {
// 根據上面redux源碼 next就是 store.dispatch
// return function (next) {
// 這個時候實際返回的dispatch就被改寫成這個了: 參考redux源碼:dispatch = compose(...chain)(store.dispatch)
// return function (action) {
// 而後在這裏傳入action creator 就能夠處理函數和對象兩種狀況下而後進行異步
// if (typeof action === 'function') {
// return action(dispatch, getState, extraArgument);
// }
// return next(action);
// }
// }
// }
  return ({ dispatch, getState }) => next => action => {
  //判斷
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;
複製代碼

reducer

做用:修改state
如何觸發:用戶每次 dispatch(action) 後,都會觸發 reducer 的執行
redux

reducer 的實質是一個函數,根據 action.type 來更新 state 並返回 nextStateapi

最後會用 reducer 的返回值 nextState 徹底替換掉原來的 statebash

注意:上面的這個 「更新」 並非指 reducer 能夠直接對 state 進行修改 Redux 規定,須先複製一份 state,在副本 nextState 上進行修改操做 例如,可使用 lodash 的 cloneDeep,也可使用 Object.assign / map / filter/ ... 等返回副本的函數

Reducer 必須是同步的純函數

具體使用

如何將react和redux結合起來? 須要react-redux

react-redux模塊用來作什麼用?
redux是一個獨立的庫,這個庫其實能夠用在angular,vue等前端框架中,因此要使用在react中須要經過react-redux作一個轉換
react-redux提供了三個api


connect()
這個方法用來負責將react組件和redux store鏈接起來
經過什麼樣子的方式把這兩個鏈接起來?
如下是connect方法須要傳入的參數

function connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?) 複製代碼

mapStateToProps:將redux中的state映射到props中去
示例:

//這一段是
export function increment() {
  return {
    type: INCREMENT_COUNTER
  };
}

export function decrement() {
  return {
    type: DECREMENT_COUNTER
  };
}

export function incrementIfOdd() {
  return (dispatch, getState) => {
    const { counter } = getState();
    if (counter % 2 === 0) {
      return;
    }

    dispatch(increment());
  };
}

export function incrementAsync(delay = 1000) {
  return (dispatch) => {
    setTimeout(() => {
      dispatch(increment());
    }, delay);
  };
}
//組件映射代碼
import { bindActionCreators } from 'redux';
//映射state的某些字段到props中
function mapStateToProps(state) {
  return {
    counter: state.counter
  };
}

//映射dispatch到組件的props
function mapDispatchToProps(dispatch) {
  return bindActionCreators(CounterActions, dispatch);
}

//將組件傳入其中映射
export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Counter);


//組件內部調用
const {
    increment,
    incrementIfOdd,
    incrementAsync,
    decrement,
    counter
  } = props;
複製代碼

以上源碼中存在一個bindActionCreators函數,這個函數來自redux函數庫
如下經過源碼講解一下幹什麼用的

function bindActionCreators(actionCreators, dispatch) {
 //若是傳入的是一個函數則調用bindActionCreator函數進行包裝,這樣返回給組件的函數只要調用就能夠觸發state更新
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }

  if (typeof actionCreators !== 'object' || actionCreators === null) {
    throw new Error(
      `bindActionCreators expected an object or a function, instead received ${ actionCreators === null ? 'null' : typeof actionCreators }. ` +
        `Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
    )
  }
  //若是是一個對象,就遍歷鍵值進行包裝而後返回組合好的對象
  const keys = Object.keys(actionCreators)
  const boundActionCreators = {}
  for (let i = 0; i < keys.length; i++) {
    const key = keys[i]
    const actionCreator = actionCreators[key]
    if (typeof actionCreator === 'function') {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  return boundActionCreators
}

//包裝action creator函數爲直接使用dispatch觸發更改的函數
function bindActionCreator(actionCreator, dispatch) {
  return function() {
    return dispatch(actionCreator.apply(this, arguments))
  }
}
複製代碼

根據源碼來看,這個函數的做用就是將dispatch和actionCreator包裝成一個接受單一參數觸發更新store的方法,簡化書寫,也就是函數的柯里化的應用。


Provider
這個標籤使得通過connect函數包裝的組件能夠得到store
正常來講,一個被connect包裝過的組件只能用在裏面

import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'

import { App } from './App'
import createStore from './createReduxStore'

const store = createStore()

ReactDOM.render(
  <Provider store={store}> <App /> </Provider>,
  document.getElementById('root')
)
複製代碼

connectAdvanced
它是一個將 React 組件鏈接到 Redux store 的函數。這個函數是 connect() 的基礎,可是對於如何把state, props, 和 dispatch 組合到最後的 props 中,則不那麼自覺得是。它不對默認值或結果的記錄作任何假設,而是將這些責任留給調用者。
意思就是不會提供默認組合的方法,而是把這些方法讓開發者實現,實現更靈活的擴展

總結

參考

github.com/kenberkeley…
react-redux.js.org/
www.redux.org.cn/

相關文章
相關標籤/搜索