在 React 的世界中,有容器组件和 UI 组件之分,在 React Hooks 出现之前,UI 组件我们可以使用函数,无状态组件来展示 UI,而对于容器组件,函数组件就显得无能为力,我们依赖于类组件来获取数据,处理数据,并向下传递参数给 UI 组件进行渲染。使用 React Hooks 相比于从前的类组件有以下几点好处:
Hook 是 React 16.8.0 版本增加的新特性,可以在函数组件中使用 state
以及其他的 React 特性
。
Hooks只能在函数式组件中使用,既无状态组件(所有钩子在用时都要先引入)
Hook 就是JavaScript 函数
,但是使用它们会有两个额外的规则:
1、只能在函数最外层调用 Hook
。不要在循环、条件判断
或者嵌套函数(子函数)
中调用。
2、只能在 React 的函数组件
中调用 Hook
。不要在其他 JavaScript 函数中调用。
3、在多个useState()
调用中,渲染之间的调用顺序
必须相同
。
使用这个 hook 能得到 redux store 的 dispatch 方法引用
,通常用于“手动” dispatch action
const dispatch = useDispatch()
例如:
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>
)
}
之前在使用 connect 的时候,我们通常使用mapDispatchToProps
和actionCreator
封装一下dispatch action
的过程,然而使用 useDispatch()
的时候却需要“手动”
地调用 dispatch()
方法。
然而,实际上 Redux Hooks 曾经提供一个叫 useActions()
的 API 起到类似于 mapDispatchToProps 和 bindActionCreators 的作用,但后来被 Dan Abramov (React和Redux的核心成员)毙掉了,主要有两个原因:
丢失了对 redux 整体数据流动的视野和理解
组件可以通过useSelector
访问store中释放的state数据
import React from "react";
import {
createStore } from "redux";
import {
Provider, useSelector, useDispatch } from "react-redux";
const initialState = {
num: 0 ,
val : 100,
list : [{
key: 1,
name: '张三'
},{
key: 2,
name: '李四'
},{
key: 3,
name: '王五'
},]
};
const reducer = (state, action) => {
switch (action.type) {
case "decrement":
return {
...state, num: state.num - 1 };
case "increment":
return {
...state, num: state.num + 1 };
default:
return state;
}
};
const store = createStore(reducer, initialState);
const App = () => {
return (
<div>
<h2>---父组件---</h2>
<Provider store={
store}>
<Child1 />
</Provider>
</div>
)
}
const Child1 = () => {
const num = useSelector(state => {
console.log('--++++--',state)
return state.num
});
const dispatch = useDispatch();
return (
<div>
<h3>---子组件---</h3>
<button onClick={
() => dispatch({
type: "increment" })}>+</button>
Number: {
num}
<button onClick={
() => dispatch({
type: "decrement" })}>-</button>
</div>
);
};
export default App;
其实,selector 这个概念跟 hooks 没有关系,并且很早就被提出了。
selector 是函数
根据 redux 的 state 查找、筛选、处理
后获得一个
或者多个派生的数据
。useSelector() 这个 hook,它的参数
就是 selector 并返回 selector 的计算结果
。
最重要的是,这个 hook 会订阅 redux store
(牢记这点
),所以每次 redux state有更新
,useSelector() 里的 selector 就会重新计算一次
,返回新的结果
,并重新渲染
当前组件。(如下面展示的)
import React from "react";
import {
createStore } from "redux";
import {
Provider, useSelector, useDispatch } from "react-redux";
const initialState = 同上
const reducer = 同上
const store = createStore(reducer, initialState);
const App = () => {
return (
console.log('---父组件---'),
<div>
<h2>---父组件---</h2>
<Provider store={
store}>
<Child1 />
</Provider>
</div>
)
}
const Child1 = () => {
const num = useSelector(state => {
return state.num
});
const dispatch = useDispatch();
return (
console.log('---子组件---'),
<div>
<h3>---子组件---</h3>
<button onClick={
() => dispatch({
type: "increment" })}>+</button>
Number: {
num}
<button onClick={
() => dispatch({
type: "decrement" })}>-</button>
</div>
);
};
export default App;
关于这个api,我也是没怎么用过,不过上网查了一波,就如字面上的意思,useStore() 这个 Hook 直接获取到了 Redux store 的引用,所以可以使用到更“底层”的API 如:
getState()
dispatch(action)
subscribe(listener)
replaceReducer(nextReducer)
其中,useStore().dispatch 其实等同于 useDispatch()
const dispatch = useDispatch();
const store = useStore();
console.log("they are equal: " + (dispatch === store.dispatch)); // true
store 中的 getState()
方法,调用它可以得到当前 redux state
,但它并不等同于 useSelector()
。
最大的区别
在于: getState()
只会获得当前时刻的 redux state
,之后state 更新
并不会
导致这个方法被再次调用
,也不会
导致重新渲染
了。
因此,根据业务需求:
需要监听 redux state 的变化
,并根据 redux state的更新而渲染不同的视图或者有不同行为
—— 那么就应该使用 useSelector Hook
一次性查询某个数据/状态
,并不关心(或刻意忽略)之后的更新
—— 那么就应该使用 useStore().getState()
平时我们使用redux的时候可能使用HOC的
形式,mapStateToProps
和mapDispatchToProps
加强组件,例如:
import React from 'react';
import {
connect } from 'react-redux';
import * as actions from '../actions/actions';
const mapStateToProps = store => ({
count: store.count
});
const mapDispatchToProps = dispatch => ({
increment: count => dispatch(actions.increment(count)),
decrement: count => dispatch(actions.decrement(count))
});
@connect(mapStateToProps,mapDispatchToProps)
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
const {
count, increment, decrement} = this.props;
return (
<div>
<h1>The count is {
count}</h1>
<button onClick={
() => increment(count)}>+</button>
<button onClick={
() => decrement(count)}>-</button>
</div>
);
}
}
export default App;
或不用@connect的形式,用下面这种连接
export default connect(mapStateToProps, mapDispatchToProps)(App);
我们也可以使用useDispatch
和useSelector
来实现类似的需求
import React from 'react';
import {
createSelector } from 'reselect';
import * as actions from '../actions/actions';
import {
useSelector, useDispatch } from 'react-redux';
const App = () => {
const dispatch = useDispatch();
const count = useSelector(createSelector(store => store.count, state => state));
return (
<div>
<h1>The count is {
count}</h1>
<button onClick={
() => dispatch(actions.increment(count))}>+</button>
<button onClick={
() => dispatch(actions.decrement(count))}>-</button>
</div>
);
}
export default App;
有的没有在全局引入所有action,可以单独引入不同action并派发
import React from 'react'
import {
useSelector, useDispatch } from 'react-redux'
import {
updateTel, updateName } from './action'
export default () => {
const formData = useSelector(state => {
return state
})
const dispatch = useDispatch()
console.log({
formData});
return <div>
form: <br/>
姓名: <input type="text" onChange={
(e) => {
dispatch(updateName(e.target.value))
}}/>
电话: <input type="tel" onChange={
(e) => {
dispatch(updateTel(e.target.value))
}}/>
</div>
}