redux-react-hook

redux-react-hook

我们知道组件之间的通信可以通过props传值的方式,但是如果有一个变量需要供全局使用,那通过这种层层传递的方式就未免有点麻烦了。
在介绍这个之前,咱们循序渐进,先从redux开始:

1. redux

reduxReact全家桶中一只重要的炸鸡,比较简单,基本用法如下:
(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())

react中使用redux例子

2. react-redux

除了使用redux之外,我们还可以使用react-redux提供的 Providerconnect 方法
官网解释:

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'),
);

参考文章
掘金参考资料

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容