02【React再造之旅】redux基础使用

写在前面

这篇文章是在我逛CSDN博客时发现"Mr_大木"大佬的《React-Redux基础(一):认识redux、redux基本用法》这篇文章后,学习完写的一片笔记,大佬的这篇文章才算真正的介绍清楚了redux的基本使用。大佬后期的文章我也会继续关注、继续学习。

redux介绍

根据redux官网的介绍,redux是一个JavaScript的状态容器,提供可预测的状态化管理。这句话最通俗的理解就是:redux是一个JS的数据存储和管理的容器,在JS里面像JSON、数组这些都是容器,通过redux我们可以操作这个容器里面存储的数据,也只能通过redux去操作这里面的数据。

在这里大家要了解下面两点:

  • redux是独立于react的,就是说它不仅仅是react特有的,在其他框架,像Vue这些,也是可以使用redux的,只不过在平时开发中我们更多的是将redux应用在react里面而已;
  • redux是一个单向数据流。

OK,我们先来解释下什么是单向数据流,先看下面这张图:

上图中涉及到的东西有四个:组件、store、action、reducer。下面就来简单介绍下这四部分在其中起什么作用:

  • store,它是redux的核心,整个应用中的状态,也就是数据,都会存储到store这个对象上,store其实是一个json对象;
  • action,它是一个对store对象中的数据修改的封装,当我们在组件中通过某个动作来修改store中的数据时,就需要使用store的dispatch向reducer传一个action过去,这个action是一个json,它里面必须包含一个type属性,以便让reducer知道想要对数据进行什么操作;
  • reducer,它其实就是一个函数,用来接收传过来的action,然后修改store中的数据,它包含两个参数,一个是state,另一个是action。state是当前状态数据,action是要对当前数据进行的修改操作;
  • 组件,就是在Vue或React里面我们定义的一个个组件,当每次action传到reducer之后,reducer回去更新store中的数据,store中的数据更新后就会导致组件的渲染。

由上面的图可以看到,我们在其中的数据流向是单向的,组件只能从store中拿数据,reducer只能接收action来修改store中的数据,action只能通过触发组件的某一操作而去执行dispatch,从而将action传到reducer中,我们并不能直接调用reducer函数或者直接修改store中的数据,这就是单向数据流。数据只能回去,不能回来。

了解了redux的基础知识后,我们接下来使用一下,对它中间的各个部件进行一个简单的认识。

redux基础使用的操作步骤

一、创建一个react项目demo

1、通过以下命令,使用react脚手架工具创建一个react项目demo:

npx create-react-app redux_basic

2、项目创建完成之后,通过以下命令进去项目根目录,然后启动项目,如下:

cd .\redux_basic\
npm start

二、安装redux

1、项目创建和启动完成之后,通过命令安装redux:

npm install redux --save-dev

安装完成之后,可以在package.json文件里看到相应的版本号,本文使用的是redux的最新版4.0.5,如下:

2、安装完成之后重新启动项目,然后我们接下来开始简单使用redux。

三、redux的基础使用

1、从这部分开始介绍下redux的基础使用,在这里我们不会将它和react结合使用,仅仅简单介绍下redux中的几个模块之间的调用关系和工作流程。

2、首先将项目代码中的入口文件index.js里面的内容注释或者清空,如下:

3、在index.js里面引入createStore,并定义一个reducer,名字自定义即可。reducer其实是一个函数,里面有两个参数:第一个参数是state,即当前的状态或者说是当前的数据;另一个是action,action表示reducer函数这一次的执行是做什么事情。state和action两个参数其实都是json形式。

import { createStore } from 'redux';
​
function default_reducer(state, action) {
​
};

4、当我们引入了createStore,定义了reducer之后,还要考虑的一件事情就是一个组件或者当前state的最初始数据从哪来的问题,所以就需要我们在reducer里面返回一个最初始数据,然后来初始化一个store,代码如下:

import { createStore } from 'redux';
​
function default_reducer(state, action) {
    return {
        name: 'X北辰北',
        url: 'www.xbeichen.cn'
    };
};
​
let store = createStore(default_reducer);

以上我们完成了一个初始化store的创建,接下来我们介绍下这个store到底能做什么事情。

5、想要知道store能做什么,我们直接将这个store打印出来就是了:

import { createStore } from 'redux';
​
function default_reducer(state, action) {
    return {
        name: 'X北辰北',
        url: 'www.xbeichen.cn'
    };
};
​
let store = createStore(default_reducer);
console.log(store); 

由上图可看出,store里面有一个getState方法,这个方法就是用来获取当前的状态数据的,即state。我们不妨使用一下这个方法看看呢:

import { createStore } from 'redux';
​
function default_reducer(state, action) {
    return {
        name: 'X北辰北',
        url: 'www.xbeichen.cn'
    };
};
​
let store = createStore(default_reducer);
//console.log(store);
​
let currentState = store.getState();
console.log(currentState);

6、同样的,它既然提供了获取state的方法,那么一定会提供修改state的方法吧,不出所料,它确实是提供了的。如果我们想修改state里的数据的话,我们就需要使用store提供的dispatch方法了。

dispatch方法只需要接收一个参数action,这个action其实也是json形式,不过它必须要有一个type属性。dispatch方法接收了action之后,它就会将这个action派发出去,然后帮助我们执行reducer。下面我们来看看代码实现:

import { createStore } from 'redux';
​
function default_reducer(state, action) {
    console.log('reducer中的action', action);   // 2、在reducer里面打印一下action,看看是否传输过来
    return {
        name: 'X北辰北',
        url: 'www.xbeichen.cn'
    };
};
​
let store = createStore(default_reducer);
//console.log(store);
​
let currentState = store.getState();  //获取state
console.log(currentState);
​
store.dispatch({        // 1、使用store的dispatch方法接收action,将其派发给reducer
    type: 'setName',
    value: 'X北辰北的博客'   //在这里除了type属性之外,其余的属性都可以自定义,这里额外定义了一个value属性
});

由上图可看到,我们的reducer函数是接收到了传过来的action的,但是这个打印动作却是执行了两次,并且只有第二次才是正确的,这是为什么呢?

这是因为我们创建store的时候,它会调用一次reducer,这个时候因为action是未定义的,所以会输出那一串奇怪的字符;第二次调用是因为我们执行了store的dispatch,给它传过去了正确的action,所以我们可以看到第二次的输出才是正常的。那原因是找到了,怎么优化呢?请看下面:

import { createStore } from 'redux';
​
function default_reducer(state, action) {
    console.log('reducer中的action', action);   // 2、在reducer里面打印一下action,看看是否传输过来
​
    switch(action.type) {   //使用switch来优化之前的写法
        case 'setName':
            return {
                ...state,
                name: action.value
            };
        default:
            return {
                name: 'X北辰北',
                url: 'www.xbeichen.cn'
            }
    }
};
​
let store = createStore(default_reducer);
//console.log(store);
​
let currentState = store.getState();  //获取修改前的state
console.log(currentState);
​
store.dispatch({        // 1、使用store的dispatch方法接收action,将其派发给reducer
    type: 'setName',
    value: 'X北辰北的博客'
});
​
let laterState = store.getState();  //获取修改后的state
console.log(laterState);

如上面所示,我们用switch语句对之前的代码进行了优化,使之就算action未定义,也能返回一个默认的state。而且通过上面的代码大家也可以看到,当我们通过dispatch修改过state之后,我们的state是修改成功的。

7、以上过程其实已经是对redux的一个基础使用,但是上述代码里有几个地方需要给大家解释一下。

7.1、首先是下面这一段代码:

    switch(action.type) {
        case 'setName':
            return {
                ...state,
                name: action.value
            };
        default:
            return {
                name: 'X北辰北',
                url: 'www.xbeichen.cn'
            }
    }

上述switch语句中的case代码块里面,我们修改state的操作并没有像下面这样去做:

    switch(action.type) { 
        case 'setName':
            state.name = action.value;
            return state;
        default:
            return {
                name: 'X北辰北',
                url: 'www.xbeichen.cn'
            }
    }

大家可以看到,我们像上述这样去修改state的时候,发现在dispatch前后的state的值是有问题的。这是因为我们这种做法违背了它的本意,当我们在reducer里面匹配到相应的action之后,返回的必须是一个全新的state数据,这才是它的本意,所以很显然,上述的做法仅仅是通过赋值操作修改了state里面name变量的值,当我们的redux管理着成千上万的值时,我们这种做法无疑会降低程序的性能,因为涉及到数据的匹配问题。但是选择返回一个全新的数据,这就没有关于数据匹配查询的性能损失,因为创建一个对象的开销跟检查一个对象并且在它内部做深度检查的开销相比的话,要小得多。所以我们reducer里面的switch语句里才会选择返回一个全新数据。

7.2、第二个问题是我们默认的state写在了default语句里面,有些隐蔽,在下面代码里我们将其提高到reducer函数参数的位置,利用默认参数的形式来做,如下:

function default_reducer(state = {     //此处使用默认参数形式
    name: 'X北辰北',
    url: 'www.xbeichen.cn'
}, action) {
    console.log('reducer中的action', action); 
​
    switch(action.type) {
        case 'setName':
            return {
                ...state,
                name: action.value
            }
        default:
            return state;
    }
};

通过上述优化,使其他人看到代码时就能了解到初始state的结构,无疑是比较友好的。

7.3、最后一个值得注意的问题就是:我们使用store.getState()的数据是不变的数据,如下所示:

import { createStore } from 'redux';
​
function default_reducer(state = {
    name: 'X北辰北',
    url: 'www.xbeichen.cn'
}, action) {
    console.log('reducer中的action', action);   // 2、在reducer里面打印一下action,看看是否传输过来
​
    switch(action.type) {   //使用switch来优化之前的写法
        case 'setName':
            return {
                ...state,
                name: action.value
            }
        default:
            return state;
    }
};
​
let store = createStore(default_reducer);
//console.log(store);
​
let currentState = store.getState();  //获取修改前的state
console.log(currentState);
​
store.dispatch({        // 1、使用store的dispatch方法接收action,将其派发给reducer
    type: 'setName',
    value: 'X北辰北的博客'
});
​
console.log(currentState);

上述代码中的currentState数据,在dispatch前后它是不变的,要想获取最新的state,我们就需要再重新getState()即可。

8、通过以上部分,给大家介绍了redux的基本使用,现在仅剩最后一个问题,就是我们通过dispatch之后,我们目前代码里是手动去打印state的最新值,那有没有一种方法:当我们通过dispatch修改state之后,它会自动执行打印或者通知程序中的某部分这个state已经改变了呢?答案肯定是有的呀,这就需要使用store里面提供的subscribe()方法来做,具体代码如下:

import { createStore } from 'redux';
​
function default_reducer(state = {
    name: 'X北辰北',
    url: 'www.xbeichen.cn'
}, action) {
    console.log('reducer中的action', action);   // 2、在reducer里面打印一下action,看看是否传输过来
​
    switch(action.type) {   //使用switch来优化之前的写法
        case 'setName':
            return {
                ...state,
                name: action.value
            }
        default:
            return state;
    }
};
​
let store = createStore(default_reducer);
//console.log(store);
​
store.subscribe(() => {   //监听到state修改后会自动执行
    console.log('subscribe', store.getState());
});
​
store.dispatch({        // 1、使用store的dispatch方法接收action,将其派发给reducer
    type: 'setName',
    value: 'X北辰北的博客'
});
​
store.dispatch({       
    type: 'setName',
    value: 'X北辰北的个人博客'
});
​
store.dispatch({       
    type: 'setName',
    value: 'X北辰北的hexo博客'
});

上述代码中我们通过使用subscribe()方法代替了手动去获取state最新值的方式,每次通过dispatch去修改state的时候,都会触发subscribe()方法的执行。

9、以上就是对整个redux的基础介绍了,接下来的文章给大家介绍下redux如何与react结合开发。

总结

其实看完整篇文章后,大家对redux的认识有以下几点就足够了:

  • 首先是redux的作用,它其实就是提供了一个全局状态树,并且redux就是用来管理这个全局状态树的;
  • redux里面有一个reducer函数,这个函数的名字可以自定义,它有两个参数:state和action。state是当前的状态数据,action是对当前状态数据所要进行的操作,两个参数都是json格式。reducer函数的作用就是接收dispatch过来的action,修改state后返回一个全新的state对象;
  • reducer函数里一般会有一个自定义的switch语句,它会根据action的type属性去执行不同的操作;在switch语句的default代码块里我们一般将默认的state返回,用来初始化state,默认的type属性是一个组合复杂的字符串;
  • 每次通过dispatch修改state之后一定要返回全新的数据,不要通过赋值操作去更新state里面某一项数据,不然会得到不符合预期的结果,并且会造成性能浪费。

附:

demo代码地址如下:

https://gitee.com/XuQianWen/redux_basic

猜你喜欢

转载自blog.csdn.net/qq_35117024/article/details/106427907
今日推荐