使用react hooks实现自己的context-redux使用帮助_hooks新手入门

注:如要运行本文的代码,请先确认自己的react版本已支持hooksreact hooks出来已经有段时间了,本文不对hooks的具体用法作介绍,而是使用hooks实现一个简易的基于context的redux使用useReducer实现初版reduxReact hooks自带了useReducer供我们使用,它接受两个参数,一是reducer函数,二是初始s

使用react hooks实现自己的context-redux使用帮助

注:如要运行本文的代码,请先确认自己的react版本已支持hooks

使用react hooks实现自己的context-redux使用帮助_hooks新手入门

react hooks出来已经有段时间了,本文不对hooks的具体用法作介绍,而是使用hooks实现一个简易的基于context的redux

使用useReducer实现初版redux

React hooks自带了useReducer供我们使用,它接受两个参数,一是reducer函数,二是初始state,并返回state和dispatch函数,如下

const [state, dispatch] = useReducer(reducer, initialState);

这个函数自己实现的话也不难,如下:

const useMyReducer = (reducer, initialState) => {
    const [state, setState] = useState(initialState);
    const dispatch = action => {
        const newState = reducer(action, state);
        setState(newState);
    };
    return [state, dispatch];
};

即将initialState作为state的初始状态传入useState,dispatch则是一个函数,它会将接受的action和state传给reducer,并获取reducer的返回值赋给state

我们先利用useReducer实现一个计数器的简单页面

reducer函数和initialState如下:

const initialState = {
    count: 0
};

const reducer = (state, action) => {
    switch (action.type) {
        case "increase":
            return { ...state, count: state.count + 1 };
        case "decrease":
            return { ...state, count: state.count - 1 };
        default:
            return state;
    }
};

计数器组件:

const Demo = () => {
    const [state, dispatch] = useReducer(reducer, initialState); 
    return (
        <div>
            counter:{state.count}
            <div>
                <button
                    onClick={() => {
                        dispatch({ type: "increase" });
                    }}
                >
                    increase
                </button>
                <button
                    onClick={() => {
                        dispatch({ type: "decrease" });
                    }}
                >
                    decrease
                </button>
            </div>
        </div>
    );
};

这就是初版的redux了,但这个redux有些问题,就是它的state和dispatch是属于自己的,其他组件并不能拿到,也就是说,如果我们的页面有两个Demo组件,它们的state是各自独立,互不影响的

将state和dispatch存在context中

为了解决上述问题,我们必须拥有一个全局状态,并将state和dispatch放入这个全局状态中。这里,我们选用context作为我们的全局状态,context在旧版React中不推荐使用,但在改进之后,官方开始推荐大家使用

我们先创建一个context:

const context = React.createContext();

为了各个组件都能拿到context的数据,我们需要有一个Provider组件包在最外层:

const Provider = props => {
    const [state, dispatch] = useReducer(reducer, initialState);
    return (
        <context.Provider value={{ state, dispatch }}>
            {props.children}
        </context.Provider>
    );
};

我们将useReducer返回的state、dispatch传入context.Provider中,让它的children都能拿到

然后,我们像下面一样用Provider包在组件外层:

<Provider>
    <Demo />
    <Demo />
</Provider>

我们删去计数器Demo组件中的:

const [state, dispatch] = useReducer(reducer, initialState);

加上通过useContext函数拿到context上的数据:

const { state, dispatch } = useContext(context);

要注意的是,传入useContext函数的context必须是我们之前通过React.createContext()创建的context

这样,即使是两个Demo组件,它们也是共用一份数据了

解决异步的问题

很显然,现在的context-redux和单纯的redux一样,只能dispatch一个对象,也就是说,这个dispatch操作是同步的,如果我们要做异步的操作呢?很简单,我们借鉴redux-thunk的方法,让dispatch可以接受函数参数

改造Provider函数组件如下:

const Provider = props => {
    const [state, origin_dispatch] = useReducer(reducer, initialState);
    const dispatch = action => {
        if (typeof action === "function") {
            return action(origin_dispatch);
        }
        return origin_dispatch(action);
    };
    return (
        <context.Provider value={{ state, dispatch }}>
            {props.children}
        </context.Provider>
    );
};

我们将userReducer函数返回的原始dispath命名为origin_dispatch,自定义dispatch函数,当action为函数的时候,我们执行action函数,并将origin_dispatch当作参数传进去;action不是函数,直接调用origin_dispatch,不做处理

我们测试一下:

const sleep = wait => {
    return new Promise(resolve => {
        setTimeout(() => resolve(), wait);
    });
};
const increaseCount = async dispatch => {
    await sleep(1000);
    dispatch({ type: "increase" });
};
<button
    onClick={() => {
        dispatch(increaseCount);
    }}
    >
    increase
</button>

increaseCount是一个异步函数,我们将它当作参数传入我们封装的新dispatch中,点击increase按钮,1s之后,计数器的数字加1,至此,我们的context-redux也支持dispatch异步操作了

最后

本文的代码,我放在了自己的github上,这是传送门

来自:https://github.com/Bowen7/Blog/issues/7

海计划公众号
(0)
上一篇 2020/03/31 01:51
下一篇 2020/03/31 01:51

您可能感兴趣的内容

  • AngularJS实现地址栏取值入门基础教程_url小白攻略

    有时候我们由如下需求1、从a.html跳转到b.html2、从a跳转时携带参数和值、3、从b.html中取出传过来的参数值在AngularJS的操作如下:在a.html中添加跳转到b.html此处注意,使用AngularJS传递参数必须使用#?传递参数,否则将无法接收。b.html中的AngularJ

    2020/03/26
  • 酷方网基础入门_面向圈子的招聘网站

    酷方网基础入门 官方网址:https://www.koofun.com/ 简介描述:面向圈子的招聘网站 酷方网是面向圈子的招聘网站,依靠圈子的力量帮求职者找工作,帮企业招聘找人才,…

    2020/03/06
  • 在小程序中实现 Mixins 方案小白基础_小程序使用说明

    在原生开发小程序的过程中,发现有多个页面都使用了几乎完全一样的逻辑。由于小程序官方并没有提供 Mixins 这种代码复用机制,所以只能采用非常不优雅的复制粘贴的方式去“复用”代码。随着功能越来越复杂,靠复制粘贴来维护代码显然不科学,于是便寻思着如何在小程序里面实现 Mixins。什么是 MixinsMixins 直译过来是“混入”的意思,顾名思义就是把可复用

    2020/03/29
  • dns被劫持问题需要网站监控来检测零基础入门_dns基础指南

    什么是网站劫持?举个现实中的例子,当我们按照自己的需求打开某一个网站之后却发现该网站的内容并不是原来的,而这一个过程就叫做dns劫持。今天小编告诉大家dns被劫持如何修复。建议大家平时还是使用网站监控工具,推荐iis7网站监控,可以24小时监控网站,一旦有异常,会马上发送邮件通知你。一、DNS劫持手动修改DNS1、在地址栏中输入:192.168.1.1(如果

    2020/03/29
  • Js数组的并集,交集,差集的实现菜鸟知识_数组入门攻略

    并集、交集、差集的概念并集:以属于A或属于B的元素为元素的集合成为A与B的并(集)交集:以属于A且属于B的元素为元素的集合成为A与B的交(集)差集:以属于A而不属于B的元素为元素的集合成为A与B的差(集)ES6的实现现在有两个数组 arr1 和 arr2let arr1 = [1,2,3,4,4]
    let arr2 = [3,4,5,6,7]1.数组的并

    2020/03/26
  • zanePerfor入门基础教程_前端性能监控系统,消息队列,高可用,集群等相关架构

    zanePerfor入门基础教程 GitHub:https://github.com/wangweianger/zanePerfor 简介描述:前端性能监控系统,消息队列,高可用,…

    2020/03/11
  • 解决小程序中webview页面多层history返回问题指南攻略_webview小白入门

    小程序开发中遇到的问题:小程序中嵌套了一个webview页面,webview页面中有静默授权(A1页面静默授权后重定向到A2页面),点小程序原生的返回按钮会返回到A1页面,然后页面就会反复静默授权预期表现:点小程序原生的返回按钮后返回到小程序上个页面解决方案:通过history.pushState添加历史记录名目,history.onpopstate监听历史

    2020/03/29
  • 几个牛X的js开发技巧小白知识_技巧入门教程

    1. 确保数组值使用 grid ,需要重新创建原始数据,并且每行的列长度可能不匹配, 为了确保不匹配行之间的长度相等,可以使用Array.fill方法。let array = Array(5).fill(‘‘);
    console.log(array); // outputs (5) [“”, “”, “”, “”, “”]2. 获取数组唯一值ES6 提供了从

    2020/03/22
  • tooltipster使用攻略_一个强大又灵活的jQuery提示框插件

    tooltipster使用攻略 GitHub:https://github.com/iamceege/tooltipster 简介描述:一个强大又灵活的jQuery提示框插件 To…

    2020/03/06
  • webpack 加载动态图片基础指南_图片入门基础教程

    webpack 加载动态图片所谓动态图片指的是接口返回的图片地址,这里的地址指的是本地的图片地址,而非网络图片的url。本地有一个 image 文件夹,存放需要用到的图片。按照接口返回的图片地址比对去加载。webpack加载图片首先想到的是 file-loader 或者 url-loader 加载图片的配置首先安装file-loader npm inst

    2020/03/23
  • html中为何经常使用<i>标签来作为小图标呢?入门攻略_图标小白常识

    很多网站都是习惯使用来代表小图标?而实际上用 元素做图标在语义上是不正确的(虽然看起来像 icon 的缩写),那么用表示小icon,是出于好记的原因吗,还是看上去有点像icon?这样不是违背了语义化的原则吗? 在语义上 标签显示斜体文本效果,它告诉浏览器将包含其中的文本以斜体字或者倾斜字体显示。从含义上可以看出并不具

    2020/04/06
  • vux基础入门教程_基于WeUI和Vue开发的移动端UI组件库,主要服务于微信页面

    vux基础入门 官方网址:https://vux.li/#/ GitHub:https://github.com/airyland/vux 简介描述:基于WeUI和Vue开发的移动…

    2020/03/05
  • canvas与svg区别小白指南_区别基础知识

    一:定义 什么是canvas?canvas画布,使用js在网页上绘制图像什么是svg?svg是可伸缩矢量图二:使用 canvas使用 var c=document.getElementById(“myCanvas”);var cxt=c.getContext(“2d”);cxt.moveTo

    2020/03/29
  • (…)这三个点在JavaScript中意味着什么?小白攻略_js知识小白知识

    这篇文章的标题来自我在Quora上被要求回答的一个问题。下面是我试图解释JavaScript中三个点的作用。希望这对于将来有相同问题的人来说可以消除围绕这个概念的迷雾。数组/对象扩展运算符假设您有以下对象:const adrian = {fullName: ‘Adrian Oprea’,occupation: ‘Software developer’,age

    2020/04/03
  • Vue之样式绑定基础知识入门_样式新手入门

    在前端开发中,设置元素的 class 列表和内联样式是基本要求。本文主要讲解Vue开发中,样式列表和内联样式的绑定,仅供学习分享使用,如果有不足之处,还请指正。概述Vue操作元素的 class 列表和内联样式是数据绑定的一个常见需求。因为它们都是属性,所以我们可以用 v-bind 处理它们:只需要通过表达式计算出字符串结果即可。不过,字符串拼接麻烦且易错。因

    2020/03/23
  • package.json中^和~的区别菜鸟知识_区别入门基础知识

    webpack 项目的package.json 文件列出了项目所依赖的插件和库,同时也给出了对应的版本说明,但是在版本说明前面还有个符号:‘^‘(插入符号)和‘~‘(波浪符号),总结了下他们之间的区别:例如:‘~‘(波浪符号):他会更新到当前minor version(也就是中间的那位数字)中最新的版本。放到我们的例子中就是:”exif-js”: “~2.3

    2020/03/22