React学习笔记——Hooks中useStore、useDispatch和useSelector的基础介绍和使用,以及两者替代connect
在 React 的世界中,有容器组件和 UI 组件之分,在 React Hooks 出现之前,UI 组件我们可以使用函数,无状态组件来展示 UI,而对于容器组件,函数组件就显得无能为力,我们依赖于类组件来获取数据,处理数据,并向下传递参数给 UI 组件进行渲染。使用 React Hooks 相比于从前的类组件有以下几点好处:代码可读性更强,原本同一块功能的代码逻辑被拆分在了不同的生命周期函数中,容
在 React 的世界中,有容器组件和 UI 组件之分,在 React Hooks 出现之前,UI 组件我们可以使用函数,无状态组件来展示 UI,而对于容器组件,函数组件就显得无能为力,我们依赖于类组件来获取数据,处理数据,并向下传递参数给 UI 组件进行渲染。使用 React Hooks 相比于从前的类组件有以下几点好处:
- 代码可读性更强,原本同一块功能的代码逻辑被拆分在了不同的生命周期函数中,容易使开发者不利于维护和迭代,通过 React Hooks 可以将功能代码聚合,方便阅读维护
- 组件树层级变浅,在原本的代码中,我们经常使用 HOC/render props 等方式来复用组件的状态,增强功能等,无疑增加了组件树层数及渲染,而在 React Hooks 中,这些功能都可以通过强大的自定义的 Hooks 来实现
Hook 是 React 16.8.0 版本增加的新特性,可以在函数组件中使用 state
以及其他的 React 特性
。
Hooks只能在函数式组件中使用,既无状态组件(所有钩子在用时都要先引入)
1、Hook 使用规则
Hook 就是JavaScript 函数
,但是使用它们会有两个额外的规则:
1、只能在函数最外层调用 Hook
。不要在循环、条件判断
或者嵌套函数(子函数)
中调用。
2、只能在 React 的函数组件
中调用 Hook
。不要在其他 JavaScript 函数中调用。
3、在多个useState()
调用中,渲染之间的调用顺序
必须相同
。
2、useDispatch
使用这个 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的核心成员)毙掉了,主要有两个原因:
- 避免将 actionCreator 的 dependency 也不得不加进 useActions() 中,导致代码冗长(dependency array)
- 在 Hooks 的世界中,直接使用 dispatch() 和 actionCreator 让代码更直接。相反,使用类似mapDispatchToProps 和 bindActionCreators 虽然看似缩短了代码量,却让开发者一定程度上
丢失了对 redux 整体数据流动的视野和理解
3、useSelector
组件可以通过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 是函数
- 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;
4、useStore
关于这个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
- 假如当前组件只是为了在 redux state中
一次性查询某个数据/状态
,并不关心(或刻意忽略)之后的更新
—— 那么就应该使用useStore().getState()
5、两者替代connect
平时我们使用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>
}
更多推荐
所有评论(0)