redux-react-hook
我们知道组件之间的通信可以通过props传值的方式,但是如果有一个变量需要供全局使用,那通过这种层层传递的方式就未免有点麻烦了。
在介绍这个之前,咱们循序渐进,先从redux开始:
1. redux
redux是React全家桶中一只重要的炸鸡,比较简单,基本用法如下:
(1)store的生成(用于存储全局状态)
// 先引入
import { createStore } from "redux";
import reducer from './reducer';
const store = createStore(reducer);
(2)reducer(根据state和action对state进行更新)
我有哪些数据,我要对这些数据做什么
// 所有的状态state
const initState = {
count: 0;
}
const reducer = (state = initState, action) => {
switch (action.type){
case 'INCREASE': return {count: state.count + 1};
case 'DECREASE': return {count: state.count - 1};
default: return state;
}
}
(3)dispatch(分发执行)
在组件中使用方式
const actions = {
increase: () => ({type: 'INCREASE'}),
decrease: () => ({type: 'DECREASE'})
}
store.dispatch(actions.increase())
2. react-redux
除了使用redux之外,我们还可以使用react-redux
提供的 Provider
和 connect
方法
官网解释:
It lets your React components read data from a Redux store, and dispatch actions to the store to update data.
它可以让组件从redux store
中读取数据,并且分发action到store中更新数据。
使用方式:
在需要使用数据的最外层包裹Provider,提供数据。
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import store from './store'
import App from './App'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
)
在组件中要想使用store,则需要使用connect
连接,connect接受四个参数:
function connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?)
①mapStateToProps
mapStateToProps?: (state, ownProps?) => Object
将外部state映射到内部props,接受state作为参数,ownProps参数是可选的,比如我可以通过内部props指定的id来获取state中某个数组arr[id]的元素。
const mapStateToProps = (state, ownProps?) => {
return {
counter: state.counter,
exceedContainer: count > 100 // 或者根据情况获取属性
}
}
当state或者ownProps变化时,mapStateToProps都会重新调用
②mapDispatchToProps
mapDispatchToProps?: Object | (dispatch, ownProps?) => Object
作用:将action作为props绑定到组件上
作为connect的第二个参数,如果是个函数类型,则最多只支持两个参数,且第一个 一定是dispatch,并且需要使用dispatch分发action
const mapDispatchToProps = dispatch => {
return {
increment: () => dispatch({ type: 'INCREMENT' }),
decrement: () => dispatch({ type: 'DECREMENT' }),
}
}
// 或者
const mapDispatchToProps = (dispatch, ownProps) => {
toggleTodo: () => dispatch(toggleTodo(ownProps.todoId))
}
///组件中使用(用第一个mapDispatchToProps)
class TestCom extends Component {
render(){
const {count, increase, decrease} = this.props;
return (<div>
<p>{count}次</p>
<button onClick={increase}> + </button>
<button onClick={decrease}> - </button>
</div>)
}
}
connect(mapStateToProps, mapDispatchToProps)(TestCom)
或者使用bindActionCreators
import React from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import globalAction from 'actions'
@connect(
state => ({count: state.count}),
dispatch => bindActionCreators({ globalAction }, dispatch)
)
class Component extends React.Component {
componentDidMount() {
// 从props中读取reducer和action
const {count, globalAction} = this.props
globalAction()
}
render() {
return <div />
}
}
③mergeProps
mergeProps?: (stateProps, dispatchProps, ownProps) => Object
如果未定义此参数,则默认使用{ ...ownProps, ...stateProps, ...dispatchProps }
这三个参数分别是mapStateToProps()
,mapDispatchToProps()
和包装器组件的props
的结果
④options
options?: Object
{
context?: Object,
pure?: boolean,
areStatesEqual?: Function,
areOwnPropsEqual?: Function,
areStatePropsEqual?: Function,
areMergedPropsEqual?: Function,
forwardRef?: boolean,
}
仅在react-redux>=6.0的版本支持
官方文档地址
3. react-redux-hook
react-hook是一种可以将class组件转为函数式组件的写法
在react-hook
中使用redux
通信就是redux-react-hook
-
StoreContext
同样的,我们还是需要上下文,必须通过StoreContext.Provider提供Redux存储
在所有组件的共同父层使用,此时app组件内的所有元素都可以访问store中的属性
import { StoreContext } from "redux-react-hook";
import store from "./store";
ReactDOM.render(
<StoreContext.Provider value={store}>
<App />
</StoreContext.Provider>,
document.getElementById("root")
);
在子组件中获取store的方式:直接通过StoreContext
import {useContext} from 'react';
import {StoreContext} from 'redux-react-hook';
const store = useContext(StoreContext);
那么store是如何创建的呢?这时候就要使用createStore
import { createStore } from "redux";
import reducer from "./reducers";
const store = createStore(reducer);
export default store;
-
useMappedState(mapState, equalityCheck?)
我们可以使用useMappedState获取reducer的状态,这个东东和mapStateToProps又有点不太一样,默认是使用===
来比较的,如果需要自定义的比较方式,则可以在第二个参数的位置传入一个比较函数。
// mapState 一定要通过useCallback来声明,否则会无无限递归
const mapState = useCallback(
state => ({
isLogin: state.isLogin
}),
[]
);
const { isLogin } = useMappedState(mapState); //这样就可以获取isLogin属性
callback的第二个参数用于比较这个参数是否发生变化,来决定是否更新函数
-
useDispatch()
使用dispatch分发action
import {useDispatch} from 'redux-react-hook';
function DeleteButton({index}) {
const dispatch = useDispatch();
const deleteTodo = useCallback(() => dispatch({type: 'delete todo', index}), [
index,
]);
return <button onClick={deleteTodo}>x</button>;
}
// action即为{type: 'delete todo', index}
自定义方式创建redux存储:
import {create} from 'redux-react-hook';
import {createStore, Store} from 'redux';
import reducer from './reducer';
export interface IState {
...
}
export type Action =
| {
type: 'add todo';
todo: string;
}
| {
type: 'delete todo';
index: number;
};
export function makeStore(): Store<IState, Action> {
return createStore(reducer, INITIAL_STATE);
}
export const {StoreContext, useDispatch, useMappedState} = create<
IState,
Action,
Store<IState, Action>
>();
// index.tsx父组件中生成store
import React from 'react';
import ReactDOM from 'react-dom';
import {StoreContext} from './Store';
import App from './App';
import {makeStore} from './Store';
const store = makeStore();
ReactDOM.render(
<StoreContext.Provider value={store}>
<App />
</StoreContext.Provider>,
document.getElementById('root'),
);