前言
redux的經典使用方式就是通過高階組件connect來將組件和store關聯起來,我們見過最多的場景也是connect和類組件的組合使用(也可以和函數組件配合使用)。但在擁抱hooks的今天,我們如何拋棄connect,通過hooks獲取到狀態和dispatch呢?
react-redux中的hook
在react-redux7.1版本之後(如果你的版本比較低又不能升級版本,可以忽略本文)正式推出了三個hook: useDispatch
, useSelector
和useStore
。這些hooks分別用於獲取dispatch
, 獲取狀態
和獲取store對象
。接下來我們來對比一下之前的寫法和hook的寫法。
1. connect版
import actions from 'xxx';
class Foo extends React.Component {
componentDidMount() {
const { dispatch } = this.props;
dispatch(getData()); // 請求數據
}
renderData() {
const { data } = this.props; // 獲取數據
return (
...
)
}
render() {
<div>
{ this.renderData() }
</div>
}
}
function mapStateToProps(state) {
return { data: state.data };
}
export default connect(
mapStateToProps, // state
{ getData: actions.getData } // actions
)(Foo);
2. hook版
本示例中只用到了useSelector和useDispatch,因爲這兩個是在組件中最常用的,useStore則要在你需要使用store對象的地方使用。
import actions from 'xxx';
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
function Foo(props) {
const dispatch = useDispatch();
const data = useSelector(state => state.data);
useEffect(() => {
dispatch(actions.getData());
}, [ dispatch ]);
const renderData = () => {
return (
...
);
};
return (
<div>
{ renderData() }
</div>
)
}
export default Foo;
3. useStore的使用方法
import { useStore } from 'react-redux';
function Foo(props) {
const store = useStore();
const state = store.getState();
...
}
注意
useSelector
接收兩個參數: selector
和equalityFn
。第一個參數就像上面示例中一樣,傳入一個函數以獲取你想要的state。第二個參數也是個函數,用於比較狀態是否發生改變,如果這個函數的執行結果爲false就表示狀態發生改變,組件將重新渲染。useSelector
源碼是這樣的:
function useSelector(selector, equalityFn = refEquality) {
invariant(selector, `You must pass a selector to useSelectors`);
const { store, subscription: contextSub } = useReduxContext();
return useSelectorWithStoreAndSubscription(
selector,
equalityFn,
store,
contextSub
)
}
我們可以看到有個默認值refEquality
, 這個函數非常簡單:
const refEquality = (a, b) => a === b
這個函數你可以自定義,react-redux也提供了一個現成的淺比較方法shallowEqual
,這個就和connect比較state的方式很類似了,你可以這麼用:
import { useSelector, shallowEqual } from 'react-redux';
const data = useSelector(state => state.data, shallowEqual);