react-redux

Connect: Extracting Data with mapStateToProps

  1. mapStateToProps 返回 state 中所需的最少數(shù)據(jù)
  2. mapStateToProps 的返回值變化蝌麸,組件會重新渲染
  3. mapStateToProps可以重新組裝數(shù)據(jù)
  4. mapStateToProps 不應(yīng)做耗時操作。耗時操作可以嘗試在 action creator 抡谐、reduce 、render 中實現(xiàn)呀页,如果確實需要在 mapStateToProps 中實現(xiàn),可以考慮使用 可記憶的 selector爆阶, 例如 reselect
  5. 如果需要返回引用對象斥扛,一定返回新對象
  6. 也可以返回函數(shù) (state, [props]) => object ,此時每一個 connect 的實例對象食茎,都會調(diào)用一次 makeMapStateToProps 函數(shù)

Connect: Dispatching Actions with mapDispatchToProps

  1. 不傳這個參數(shù)蒂破,默認會將 dispatch 添加到 props 中。如果傳了别渔,dispatch 默認不會傳入 props 中附迷。如果需要,需顯式返回哎媚。
  2. mapDispatchToProps 支持 Function 和 Object 兩種形式喇伯。推薦使用 plain object ,除非有特殊情況拨与。
  3. 不建議在 component 中直接調(diào)用 store.dispatch 或者通過 context 直接獲取 dispatch

函數(shù)形式:

const increment = () => ({ type: 'INCREMENT' })
const decrement = () => ({ type: 'DECREMENT' })
const reset = () => ({ type: 'RESET' })

const mapDispatchToProps = dispatch => {
return {
    // dispatching actions returned by action creators
    increment: () => dispatch(increment()),
    decrement: () => dispatch(decrement()),
    reset: () => dispatch(reset())
  }
}

bindActionCreators 形式:

import { bindActionCreators } from 'redux'
// ...

function mapDispatchToProps(dispatch) {
  return bindActionCreators({ increment, decrement, reset }, dispatch)
}

// component receives props.increment, props.decrement, props.reset
connect(
    null,
    mapDispatchToProps
)(Counter)

Plain Object 形式:
當(dāng)每一個字段都是對象的時候稻据,redux 會假定這個對象就是一個 action creator。但是此時 props 中就沒有 dispatch 了买喧。

import {increment, decrement, reset} from "./counterActions";

const actionCreators = {
    increment,
    decrement,
    reset
}

export default connect(mapState, actionCreators)(Counter);

// or
export default connect(
  mapState,
  { increment, decrement, reset }
)(Counter);

如何獲取 Store

  1. 使用 useStore Hook
  2. 理解 Context 的使用
    react-redux 是通過 react 的 context 特性實現(xiàn)向深層嵌套的組件傳遞 redux store 的捻悯。React redux version 6 中,context 是 ReactReduxContext 淤毛,由 React.createContext() 創(chuàng)建今缚。
    <Provider> 實際是 <ReactReduxContext.Provider>
    connect 實際是用 <ReactReduxContext.Consumer> 獲取數(shù)據(jù)
  3. 自定義 context
  4. 多個 store
  5. 直接使用 ReactReduxContext
import { ReactReduxContext } from 'react-redux'

// in your connected component
function MyConnectedComponent() {
    return (
    <ReactReduxContext.Consumer>
     {({ store }) => {
    // do something useful with the store, like passing it to a child
    // component where it can be used in lifecycle methods
    }}
    </ReactReduxContext.Consumer>
  )
}

-----------API

connect

可以自定義 compare 函數(shù),默認都是淺比較
{
context?: Object,
pure?: boolean,
areStatesEqual?: Function,
areOwnPropsEqual?: Function,
areStatePropsEqual?: Function,
areMergedPropsEqual?: Function,
forwardRef?: boolean,
}

Provider

connectAdvanced

connect 的一個底層實現(xiàn),一般用不到低淡。

batch()

組合多個 store 的改變在一個事件循環(huán)中姓言,所以 UI 只重繪一次

import { batch } from 'react-redux'

function myThunk() {
    return (dispatch, getState) => {
      // should only result in one combined re-render, not two
      batch(() => {
        dispatch(increment())
        dispatch(increment())
      })
    }
}

Hooks

  1. useSelector
  • 從 React Redux v7 開始,由于使用了 batch behavior 查牌,在同一個組件中一個 action 導(dǎo)致的多個 useSelector 只會導(dǎo)致一次重繪事期。
  • useSelector 應(yīng)執(zhí)行快滥壕,避免耗時操作
  • 具有緩存機制纸颜,比較機制是 ===
  • 可以手動傳入比較機制
import { shallowEqual, useSelector } from 'react-redux'

// later
const selectedData = useSelector(selectorReturningObject, shallowEqual)
  • 配合 reselect 使用時,當(dāng)一個 selector 只在一個組件中使用時绎橘,確保 selector 是同一個實例胁孙;當(dāng) selector 要在多個組件或者同個組件的多個實例中使用時,確保 selector 是多個實例称鳞。因為 reselect 的 selector 是根據(jù)參數(shù)的變化來緩存計算結(jié)果的涮较。
import React, { useMemo } from 'react'
import { useSelector } from 'react-redux'
import { createSelector } from 'reselect'

const makeSelectCompletedTodosCount = () =>
    createSelector(
      state => state.todos,
      (_, completed) => completed,
      (todos, completed) =>
      todos.filter(todo => todo.completed === completed).length
    )

export const CompletedTodosCount = ({ completed }) => {
const selectCompletedTodosCount =         useMemo(makeSelectCompletedTodosCount, [])

  const matchingCount = useSelector(state =>
      selectCompletedTodosCount(state, completed)
  )

  return <div>{matchingCount}</div>
}

export const App = () => {
    return (
      <>
      <span>Number of done todos:</span>
      <CompletedTodosCount completed={true} />
      <span>Number of unfinished todos:</span>
      <CompletedTodosCount completed={false} />
    </>
  )
}
  1. useDispatch
  • 只要 store 對象不變,useDispatch 的返回值 dispatch 就不會變冈止。一般來說狂票,在應(yīng)用中,是不變的熙暴。但 React hooks lint rules 不知道闺属,所以 dependency 里面可以加上 dispatch
  • 一個利用 useMemo 的優(yōu)化示例
import React, { useCallback } from 'react'
import { useDispatch } from 'react-redux'

export const CounterComponent = ({ value }) => {
    const dispatch = useDispatch()
    const incrementCounter = useCallback(
      () => dispatch({ type: 'increment-counter' }),
      [dispatch]
    )

    return (
      <div>
        <span>{value}</span>
        <MyIncrementButton onIncrement={incrementCounter} />
      </div>
    )
  }

  export const MyIncrementButton = React.memo(({ onIncrement }) => (
      <button onClick={onIncrement}>Increment counter</button>
  ))

這個例子中慌盯,React.useMemo 的使用可以避免 onIncrement 的變化導(dǎo)致 MyIncrementButton 的重繪。

  1. useStore
    這應(yīng)該盡量少用掂器,優(yōu)先考慮 useSelector
  2. 自定義 context 的相關(guān) hooks 的使用
    createStoreHook(MyContext)
    createDispatchHook(MyContext)
    createDispatchHook(MyContext)
警告

useSelector 可能會存在 Stale Props and "Zombie Children" 的問題亚皂。(舊的 Props 和 僵尸子節(jié)點)

性能

使用 connect() 的組件,如果 state 和 props 都沒有改變国瓮,即使父組件重新渲染灭必,子組件也不會重新渲染。但是 useSelector 的函數(shù)組件乃摹,子組件會隨父組件一起重新渲染禁漓,即使 state 和 props 都沒有改變。
為了更好的性能優(yōu)化峡懈,我們可以使用 React.memo 來解決這個問題:

const CounterComponent = ({ name }) => {
  const counter = useSelector(state => state.counter)
  return (
    <div>
      {name}: {counter}
    </div>
  )
}

export const MemoizedCounterComponent = React.memo(CounterComponent)

Hooks Recipes

  • useActions:bindActionCreators 在函數(shù)組件中的替代璃饱,需要自己復(fù)制黏貼定義。
import { bindActionCreators } from 'redux'
import { useDispatch } from 'react-redux'
import { useMemo } from 'react'

export function useActions(actions, deps) {
  const dispatch = useDispatch()
  return useMemo(
    () => {
      if (Array.isArray(actions)) {
        return actions.map(a => bindActionCreators(a, dispatch))
      }
      return bindActionCreators(actions, dispatch)
    },
    deps ? [dispatch, ...deps] : [dispatch]
  )
}
  • useShallowEqualSelector
import { useSelector, shallowEqual } from 'react-redux'

export function useShallowEqualSelector(selector) {
  return useSelector(selector, shallowEqual)
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末肪康,一起剝皮案震驚了整個濱河市荚恶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌磷支,老刑警劉巖谒撼,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異雾狈,居然都是意外死亡廓潜,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進店門善榛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辩蛋,“玉大人,你說我怎么就攤上這事移盆〉吭海” “怎么了?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵咒循,是天一觀的道長据途。 經(jīng)常有香客問我,道長叙甸,這世上最難降的妖魔是什么颖医? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮裆蒸,結(jié)果婚禮上熔萧,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好佛致,可當(dāng)我...
    茶點故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布遂赠。 她就那樣靜靜地躺著,像睡著了一般晌杰。 火紅的嫁衣襯著肌膚如雪跷睦。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天肋演,我揣著相機與錄音抑诸,去河邊找鬼。 笑死爹殊,一個胖子當(dāng)著我的面吹牛蜕乡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播梗夸,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼层玲,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了反症?” 一聲冷哼從身側(cè)響起辛块,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎铅碍,沒想到半個月后润绵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡胞谈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年尘盼,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烦绳。...
    茶點故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡卿捎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出径密,到底是詐尸還是另有隱情午阵,我是刑警寧澤,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布睹晒,位于F島的核電站趟庄,受9級特大地震影響括细,放射性物質(zhì)發(fā)生泄漏伪很。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一奋单、第九天 我趴在偏房一處隱蔽的房頂上張望锉试。 院中可真熱鬧,春花似錦览濒、人聲如沸呆盖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽应又。三九已至宙项,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間株扛,已是汗流浹背尤筐。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留洞就,地道東北人盆繁。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像旬蟋,于是被迫代替她去往敵國和親油昂。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 我們已經(jīng)詳細介紹了Action倾贰,Reducer冕碟,Store和它們之間的流轉(zhuǎn)關(guān)系。Redux的基礎(chǔ)知識差不多也介紹完...
    張歆琳閱讀 3,734評論 1 17
  • 英文文檔鏈接 API <Provider store> 使得層級之下的組件可以通過connect()函數(shù)訪問到Re...
    txwslyf閱讀 1,483評論 0 0
  • 注意:文章很長匆浙,只想了解邏輯而不深入的鸣哀,可以直接跳到最后的總結(jié)部分。 初識 首先吞彤,從它暴露對外的API開始 現(xiàn)在對...
    stonehank閱讀 608評論 0 0
  • 前言 本文 有配套視頻我衬,可以酌情觀看。 文中內(nèi)容因各人理解不同饰恕,可能會有所偏差挠羔,歡迎朋友們聯(lián)系我討論。 文中所有內(nèi)...
    珍此良辰閱讀 11,898評論 23 111
  • 今天感恩節(jié)哎埋嵌,感謝一直在我身邊的親朋好友破加。感恩相遇!感恩不離不棄雹嗦。 中午開了第一次的黨會范舀,身份的轉(zhuǎn)變要...
    迷月閃星情閱讀 10,559評論 0 11