Redux學(xué)習(xí)筆記

Redux并不是只能在React應(yīng)用中使用,而是可以在一般的應(yīng)用中使用。
一、首先來看一下redux的工作流:

ca4884ebcb8e8253b0948de9707e865.png

ReactComponents可以看做'借書人',ActionCreators可以看做‘我要借本書’,Store可以看做圖書管理員,Reducers可以看做'電腦/手冊(cè)'

  • store是唯一的,只有store能夠改變自己的內(nèi)容。唯一改變state的方法是觸發(fā)action
  • 借書人(ReactComponents)通過觸發(fā)action告訴圖書管理員(Store)我要借本書(ActionCreators)。圖書管理員(Store)收到消息后通過查閱電腦/手冊(cè)(Reducers),將書(state)借給借書人(ReactComponents)
  • action實(shí)質(zhì)上是一個(gè)對(duì)象。我們通過ActionCreators統(tǒng)一創(chuàng)建action(定義很多個(gè)方法,每個(gè)方法都導(dǎo)出這個(gè)action對(duì)象)
const action = {
  type: INPUT_CHANGE,
  value: e.target.value
}
// store.dispatch(action)
// actionCreators.js
export const onInputChange = (value) => ({
    type: INPUT_CHANGE,
    value
})
// store.dispatch(onInputChange(e.target.value))
  • store.dispatch(action) 當(dāng)store接收到action后,會(huì)將previousState和action自動(dòng)轉(zhuǎn)發(fā)給reducer, reducer處理完成相應(yīng)的邏輯后將新修改的newState返回給store。store接收到reducer傳來的新的state后會(huì)替換掉原來舊的state,從而實(shí)現(xiàn)store里的state的更新
  • reducer可以接收state,但絕不能修改state。所以要將從store接收到的state深拷貝一份,返回新更改的state
  • reducer必須是純函數(shù)。純函數(shù)指的是,給定固定的輸入,就一定會(huì)有固定的輸出。同時(shí)不會(huì)有任何副作用。reducer里不能有new Date()、setTimeOut、 ajax請(qǐng)求等
const defaultStore = {
    inputValue: ''
}
export default (state = defaultStore, action) => {
    if (action.type === INPUT_CHANGE) {
       const newState = JSON.parse(JSON.stringify(state))
       newState.inputValue = action.value
       return newState
    }
  return state
}
  • store.subscribe()這個(gè)方法是在組件里訂閱store.只要store一改變,subscribe里的方法就會(huì)自動(dòng)執(zhí)行
 constructor (props) {
    super(props)
    this.state = store.getState()
    store.subscribe(this.handelStoreChange)
  }
 handelStoreChange () {
    this.setState(store.getState())
 }

二、store中的方法
Redux中的store是一個(gè)保存整個(gè)應(yīng)用state對(duì)象樹的對(duì)象,其中包含了幾個(gè)方法,它的原型如下:

type Store = {
  dispatch: Dispatch
  getState: () => State
  subscribe: (listener: () => void) => () => void
  replaceReducer: (reducer: Reducer) => void
}
  • dispatch 用于發(fā)送action(動(dòng)作)使用的的方法
  • getState 取得目前state的方法
  • subscribe 注冊(cè)一個(gè)回調(diào)函數(shù),當(dāng)state有更動(dòng)時(shí)會(huì)調(diào)用它
  • replaceReducer 高級(jí)API,用于動(dòng)態(tài)加載其它的reducer,一般情況不會(huì)用到

三、使用redux-thunk中間件實(shí)現(xiàn)ajax請(qǐng)求

  • redux-thunks是redux的中間件。我們?cè)趧?chuàng)建store的時(shí)候,就使用中間件。
import {createStore, applyMiddleware, compose} from 'redux'
import thunk from 'redux-thunk'
import reducer from './reducer'
const Combine =  compose(applyMiddleware(thunk), window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())
var store = createStore(reducer, Combine)
export default store
  • 使用了redux-thunk之后,actionCreater可以return出去一個(gè)函數(shù)(正常情況下return一個(gè)對(duì)象)
export const initListData = (list) => ({
    type: INIT_LIST,
    list
})
export const getListData = () => {
    return dispatch => {
        axios.get('/api/todolist')
        .then((res) => {
            const data = res.data
            dispatch(initListData(data))
        })
    }
}
  • 只有使用了thunk這個(gè)中間件,action才能是個(gè)函數(shù),當(dāng)store發(fā)現(xiàn)action是個(gè)函數(shù)時(shí),會(huì)自動(dòng)執(zhí)行這個(gè)函數(shù)
 componentDidMount () {
    // axios.get('/api/todolist')
    // .then((res) => {
    //   console.log(res.data);  
    //   store.dispatch(initListData(res.data))
    // })
    store.dispatch(getListData())  
  }

當(dāng)我們把異步請(qǐng)求放在組件里的生命周期里時(shí),隨著代碼量的增加,這個(gè)生命周期函數(shù)可能會(huì)變得越來越復(fù)雜,組件變得越來越大。所以建議還是將這種復(fù)雜的業(yè)務(wù)邏輯異步代碼拆分到一個(gè)地方去管理。
現(xiàn)在借助redux-thunk 將異步請(qǐng)求放在actionCreator中去管理。
放在這里又帶來一個(gè)好處,就是自動(dòng)化測(cè)試變得很簡(jiǎn)單,比測(cè)試組件的生命周期函數(shù)簡(jiǎn)單的多。

  • redux中間件指的是action和store之間。action通過dispatch方法被傳遞給store。實(shí)際上中間件就是對(duì)dispatch方法的一個(gè)封裝
    d7f70ee27ee955877023b3ec74db848.png

最原始的dispatch方法,接收到一個(gè)對(duì)象之后會(huì)把這個(gè)對(duì)象傳遞給store.當(dāng)有了中間件(對(duì)dispatch方法進(jìn)行升級(jí)之后),當(dāng)傳遞一個(gè)函數(shù)時(shí),dispatch不會(huì)把這個(gè)函數(shù)直接傳給store,而是先執(zhí)行這個(gè)函數(shù).所以dispatch會(huì)根據(jù)參數(shù)的不同做不同的事情。

四、使用redux-saga中間件實(shí)現(xiàn)ajax請(qǐng)求

  • redux-thunk中間件是把異步操作放在action里,redux-suga是單獨(dú)把異步邏輯拆分出來放到一個(gè)文件里去管理.
  • saga里面都是generator函數(shù)
  • saga.js中會(huì)接收到每一次派發(fā)出的action
  • takeEvery函數(shù)接收到相應(yīng)的action類型,會(huì)執(zhí)行第二個(gè)參數(shù)的方法,將相應(yīng)的異步邏輯寫在第二個(gè)參數(shù)的方法中。建議寫成generator函數(shù)。
import {createStore, applyMiddleware, compose} from 'redux'
import createSagaMiddleware from 'redux-saga'
import reducer from './reducer'
import todoSaga from './saga'
const sagaMiddleware = createSagaMiddleware()
const enhancers =  compose(applyMiddleware(sagaMiddleware),window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())
var store = createStore(reducer, enhancers)
sagaMiddleware.run(todoSaga)
export default store
componentDidMount () {
  store.dispatch(getListData())  
}
export const initListData = (list) => ({
    type: INIT_LIST,
    list
})
export const getListData = () => ({
    type: GRT_INIT
})
import {put, takeEvery} from 'redux-saga/effects'
import {GRT_INIT} from './actiontypes'
import {initListData} from './actionCreators'
import axios from 'axios'
function* asyncGetList () {
    try {
        const res = yield axios.get('/api/todolist')
        const action = initListData(res.data)
        yield put(action)
    } catch (err) {
        console.error(err)     
    }
}
function* todoSaga () {
    yield takeEvery(GRT_INIT, asyncGetList)
}
export default todoSaga
  • takeEvery和takeLatest的區(qū)別:
    takeEvery每次觸發(fā)都會(huì)執(zhí)行,不會(huì)中斷;
    takeLatest連續(xù)觸發(fā),只執(zhí)行最后一次觸發(fā)的結(jié)果(比如點(diǎn)擊提交按鈕,避免提交多次)
  • 調(diào)用promise的地方用call語(yǔ)句包裹起來
function* fetchUser() {
  const user = yield call(axios.get, 'https://jsonplaceholder.typicode.com/users');
  console.log(user)
}
  • 并發(fā)請(qǐng)求可以使用yeild all([ ])把多個(gè)請(qǐng)求包起來

    image.png

  • 當(dāng)saga函數(shù)特別多時(shí)如何組織文件?
    方法一:


    image.png

使用fork關(guān)鍵字運(yùn)行函數(shù),上面例子相當(dāng)于:


image.png

因?yàn)閡serSagas打印出來是個(gè)對(duì)象,值是watchFetchUser函數(shù)

方法二:在各自文件入口維護(hù)好sage并導(dǎo)出來

image.png

然后在根saga文件中使用yeild all [ ]
image.png

五、react-redux的使用

  • provider將store提供給內(nèi)部所有的組件。渲染根組件時(shí)使用即可。

<Provider /> 是由 React Redux 提供的高階組件,用來讓你將 Redux 綁定到 React

import React from 'react'
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import App from './App';

let store = createStore(todoApp)

ReactDOM .render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)
  • 在組件里,使用connect,讓組件和store做連接。
  • connect接收3個(gè)參數(shù),第三個(gè)參數(shù)是要和store連接的組件。mapStateToProps這個(gè)映射規(guī)則,是將store里的state映射為組件里的props。mapDispatchToProps將store.dispatch方法掛載到props上。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 鶥熙閱讀 180評(píng)論 0 0
  • 春天的早晨在鳥兒的叫聲和徐徐清風(fēng)下呈現(xiàn),格外清新和靜謐。每天寫文字,都不是文思泉涌,有時(shí)能夠一氣呵成,有時(shí)才盡詞窮...
    鄒艷麗閱讀 146評(píng)論 0 0
  • 平行Amal 熒幕靚仔 2013年,作為繼《孤男寡女》《瘦身男女》系列愛情喜劇“都市男女”的第三部, 杜琪峰在《單...
    平行Amal閱讀 636評(píng)論 0 0
  • 開始日更,希望能當(dāng)做日記寫。今天是十一月二十一日,天氣晴朗,偶有小風(fēng)。高數(shù)課上地有點(diǎn)迷,講了很久的習(xí)題,有很多...
    不會(huì)說話的小星星閱讀 169評(píng)論 0 2
  • 上周的大河基因試講中,我聽到了姑娘說,她被虐了~那個(gè)時(shí)候的自己內(nèi)心也是滿滿的情愫,的確,大河是一個(gè)讓人情感淪陷的地...
    雪寶日志閱讀 1,665評(píng)論 1 3

友情鏈接更多精彩內(nèi)容