React-redux 7.1發版啦。react
由於在新的項目中用到了hooks,可是用的時候react-redux還處於alpha.x
版本的狀態。用不了最新的API,感受不是很美妙。好在,這兩天發佈了7.1版本。git
如今來看看怎麼用這個新的API。github
useSelector()
const result : any = useSelector(selector : Function, equalityFn? : Function)
複製代碼
這個是幹啥的呢?就是從redux的store對象中提取數據(state)。redux
注意: 由於這個可能在任什麼時候候執行屢次,因此你要保持這個selector是一個純函數。api
這個selector方法相似於以前的connect的mapStateToProps參數的概念。而且useSelector
會訂閱store, 當action被dispatched的時候,會運行selector。數組
固然,僅僅是概念和mapStateToProps類似,可是確定有不一樣的地方,看看selector和mapStateToProps的一些差別:緩存
useSelector
的返回結果。useSelector()
將對前一個selector結果值和當前結果值進行淺比較。若是不一樣,那麼就會被re-render。 反之亦然。useSelector()
默認使用===
(嚴格相等)進行相等性檢查,而不是淺相等(==
)。你可能在一個組件內調用useSelector
屢次,可是對useSelector()
的每一個調用都會建立redux store的單個訂閱。因爲react-reduxv7版本使用的react的批量(batching)更新行爲,形成同個組件中,屢次useSelector返回的值只會re-render一次。性能優化
當函數組件渲染時,會調用提供的selector函數,而且從useSelector
返回其結果。(若是selector運行且沒有更改,則會返回緩存的結果)。閉包
上面有說到,只當對比結果不一樣的時候會被re-render。從v7.1.0-alpha.5開始,默認比較是嚴格比較(===
)。這點於connect的時候不一樣,connect使用的是淺比較。這對如何使用useSelector()
有幾個影響。jsp
使用mapState
,全部單個屬性都在組合對象中返回。返回的對象是不是新的引用並不重要 - connect()
只比較各個字段。使用useSelector
就不行了,默認狀況下是,若是每次返回一個新對象將始終進行強制re-render。若是要從store中獲取多個值,那你能夠這樣作:
useSelector()
調用屢次,每次返回一個字段值。
使用Reselect或相似的庫建立一個記憶化(memoized) selector,它在一個對象中返回多個值,但只在其中一個值發生更改時才返回一個新對象。
使用react-redux 提供的shallowEqual
函數做爲useSelector
的equalityFn
參數。
就像下面這樣:
import { shallowEqual, useSelector } from 'react-redux'
// later
const selectedData = useSelector(selectorReturningObject, shallowEqual)
複製代碼
上面作了一些基本的闡述,下面該用一些例子來加深理解。
基本用法
import React from 'react'
import { useSelector } from 'react-redux'
export const CounterComponent = () => {
const counter = useSelector(state => state.counter)
return <div>{counter}</div>
}
複製代碼
經過閉包使用props來肯定要提取的內容:
import React from 'react'
import { useSelector } from 'react-redux'
export const TodoListItem = props => {
const todo = useSelector(state => state.todos[props.id])
return <div>{todo.text}</div>
}
複製代碼
對於memoizing不是很瞭解的,能夠通往此處瞭解。
當使用如上所示的帶有內聯selector的useSelector
時,若是渲染組件,則會建立selector的新實例。只要selector不維護任何狀態,這就能夠工做。可是,記憶化(memoizing) selectors 具備內部狀態,所以在使用它們時必須當心。
當selector僅依賴於狀態時,只需確保它在組件外部聲明,這樣一來,每一個渲染所使用的都是相同的選擇器實例:
import React from 'react'
import { useSelector } from 'react-redux'
import { createSelector } from 'reselect' //上面提到的reselect庫
const selectNumOfDoneTodos = createSelector(
state => state.todos,
todos => todos.filter(todo => todo.isDone).length
)
export const DoneTodosCounter = () => {
const NumOfDoneTodos = useSelector(selectNumOfDoneTodos)
return <div>{NumOfDoneTodos}</div>
}
export const App = () => {
return (
<>
<span>Number of done todos:</span>
<DoneTodosCounter />
</>
)
}
複製代碼
若是selector依賴於組件的props,可是隻會在單個組件的單個實例中使用,則狀況也是如此:
import React from 'react'
import { useSelector } from 'react-redux'
import { createSelector } from 'reselect'
const selectNumOfTodosWithIsDoneValue = createSelector(
state => state.todos,
(_, isDone) => isDone,
(todos, isDone) => todos.filter(todo => todo.isDone === isDone).length
)
export const TodoCounterForIsDoneValue = ({ isDone }) => {
const NumOfTodosWithIsDoneValue = useSelector(state =>
selectNumOfTodosWithIsDoneValue(state, isDone)
)
return <div>{NumOfTodosWithIsDoneValue}</div>
}
export const App = () => {
return (
<>
<span>Number of done todos:</span>
<TodoCounterForIsDoneValue isDone={true} />
</>
)
}
複製代碼
可是,若是selector被用於多個組件實例而且依賴組件的props,那麼你須要確保每一個組件實例都有本身的selector實例(爲何要這樣?看這裏):
import React, { useMemo } from 'react'
import { useSelector } from 'react-redux'
import { createSelector } from 'reselect'
const makeNumOfTodosWithIsDoneSelector = () =>
createSelector(
state => state.todos,
(_, isDone) => isDone,
(todos, isDone) => todos.filter(todo => todo.isDone === isDone).length
)
export const TodoCounterForIsDoneValue = ({ isDone }) => {
const selectNumOfTodosWithIsDone = useMemo(
makeNumOfTodosWithIsDoneSelector,
[]
)
const numOfTodosWithIsDoneValue = useSelector(state =>
selectNumOfTodosWithIsDoneValue(state, isDone)
)
return <div>{numOfTodosWithIsDoneValue}</div>
}
export const App = () => {
return (
<>
<span>Number of done todos:</span>
<TodoCounterForIsDoneValue isDone={true} />
<span>Number of unfinished todos:</span>
<TodoCounterForIsDoneValue isDone={false} />
</>
)
}
複製代碼
useDispatch()
const dispatch = useDispatch()
複製代碼
這個Hook返回Redux store中對dispatch
函數的引用。你能夠根據須要使用它。
用法和以前的同樣,來看個例子:
import React from 'react'
import { useDispatch } from 'react-redux'
export const CounterComponent = ({ value }) => {
const dispatch = useDispatch()
return (
<div>
<span>{value}</span>
<button onClick={() => dispatch({ type: 'increment-counter' })}>
Increment counter
</button>
</div>
)
}
複製代碼
當使用dispatch
將回調傳遞給子組件時,建議使用useCallback
對其進行記憶,不然子組件可能因爲引用的更改進行沒必要要地呈現。
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>
))
複製代碼
useStore()
const store = useStore()
複製代碼
這個Hook返回redux <Provider>
組件的store
對象的引用。
這個鉤子應該不長被使用。useSelector
應該做爲你的首選。可是,有時候也頗有用。來看個例子:
import React from 'react'
import { useStore } from 'react-redux'
export const CounterComponent = ({ value }) => {
const store = useStore()
// 僅僅是個例子! 不要在你的應用中這樣作.
// 若是store中的state改變,這個將不會自動更新
return <div>{store.getState()}</div>
}
複製代碼
前面說了,selector的值改變會形成re-render。可是這個與connect
有些不一樣,useSelector()
不會阻止組件因爲其父級re-render而re-render,即便組件的props沒有更改。
若是須要進一步的性能優化,能夠在React.memo()
中包裝函數組件:
const CounterComponent = ({ name }) => {
const counter = useSelector(state => state.counter)
return (
<div>
{name}: {counter}
</div>
)
}
export const MemoizedCounterComponent = React.memo(CounterComponent)
複製代碼
useActions()
這個是alpha的一個hook,可是在alpha.4中聽取Dan的建議被移除了。這個建議是基於「binding actions creator」在基於鉤子的用例中沒啥特別的用處,而且致使了太多的概念開銷和語法複雜性。
你可能更喜歡直接使用useDispatch。你可能也會使用Redux的bindActionCreators
函數或者手動綁定他們,就像這樣: const boundAddTodo = (text) => dispatch(addTodo(text))
。
可是,若是你仍然想本身使用這個鉤子,這裏有一個現成的版本,它支持將action creator做爲單個函數、數組或對象傳遞進來。
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] : deps)
}
複製代碼
useShallowEqualSelector()
import { shallowEqual } from 'react-redux'
export function useShallowEqualSelector(selector) {
return useSelector(selector, shallowEqual)
}
複製代碼
如今在hooks組件裏,咱們不須要寫connect
, 也不須要寫mapStateToProps
, 也不要寫mapDispatchToProps
了,只須要一個useSelector
。
你們對於這個版本有沒有感受不滿意的地方?
原文:簡書: react-redux@7.1的api,用於hooks
代碼註釋:v7.1 code