项目中redux的版本是:"redux": "^4.0.0",
react-redux的版本:"react-redux": "^6.0.0",
react版本:"react": "^16.6.2",
createStore
相信大家在项目中都会应用到 redux,那么在应用的过程中可能会碰到一些问题,这里也是在项目中遇到的一些问题的一个记录,仅供参考
首先,我们来梳理下 redux 的核心 createStore。
- createStore 是一个函数,其接收一个参数:reducer,也就是我们所说的管理员
- 在函数内有三个方法,分别是 getState, dispatch, subsc-ribe
- dispatch 方法接收一个 action 对象,负责通知管理员,让管理员去对状态容器 store 中的 state 进行修改
- dispatch 方法还负责将之前订阅放入队列中的函数一一取出并执行
- subscribe 方法负责将调用函数时传入的要执行的函数 fn 放入到队列中,同时返回一个可取消这个函数 fn 的新函数
- getState 方法负责返回一个负责好的 state 对象
具体代码如下:
在这里我们可以做简单的分析:
- 具体的动作通过 dispatch 方法交给 reducer 管理员进行处理,管理员会返回新的 state 状态值
- 想要获取最新的 state 状态值通过 getState 方法获取,此时获取到的状态值为一个副本
- 这样做到组件获取数据和修改数据的隔离,无法直接对状态 state 数据进行修改
- 采用队列和订阅发布模式来执行新的更新
组件和状态关联
到现在,我们比较清晰的理解如何获取 state 状态值,并可以通过 dispatch 派发一个 action 动作对象来完成 state 状态值的更新 但是现在有一个问题,组件触发更新的时机在哪儿?
答案就在 subscribe 函数上
- subscribe 主要任务是订阅函数,将要执行的函数 fn 放入队列中,并返回一个解绑函数
- 在 dispatch 方法中管理员返回最新的 state 对象之后将执行队列中的函数
那么我们就可以这么做:
- 每个组件在合适的生命周期方法中订阅状态,可通过 subscribe 方法去订阅最新的状态
- 状态值在 reducer 管理员的修改下发生了变化,并且执行队列中的方法,此时 stae 状态值已经是最新
- 状态值为最新值,而此时可以通过 setState 方法去更新最新的状态
项目应用
清晰的目录结构
在项目里面我们通常会把reducer和action分开,将它们分别存放于不同的文件夹,每个文件夹都有不同的职责。
所以我们会对其对应的内容进行一个划分,这样的话,有利于我们管理以及后续代码的一个维护。
每个文件夹中都有对应的reduce或者是action的文件,比如说有关于用户的一些处理我们可以放在action文件夹下面的account文件里面。
比如这样的目录结构:
actions 和 reducers
关于Type的定义:也就是我们通常所说的所触发的那个type是什么?
同样的我们会把这些类型都会定义的action文件夹对应的文件中。
关于类型的几种定义分类:
通常我们如果说要发起Ajax请求的时候,我们会定这样三种类型。
第1个是关于请求也就是request:
const ACCOUNT_GET_ACCOUNTS_INFO_REQUEST = 'ACCOUNT_GET_ACCOUNTS_INFO_REQUEST'
第2个是关于请求的状态是否成功,如果是成功的那个状态的话,我们会定义成success:
const ACCOUNT_GET_ACCOUNTS_INFO_SUCCESS = 'ACCOUNT_GET_ACCOUNTS_INFO_SUCCESS'
第3个是请求状态为失败,这时候我们定义成FAILED:
const ACCOUNT_GET_ACCOUNTS_INFO_FAILED = 'ACCOUNT_GET_ACCOUNTS_INFO_FAILED'
action
同时我们会有一个对应的一个action,动作函数,动作函数接收一个dispatch这样的一个参数。
所以说这个函数非常的简单。只是用来触发这样一个动作的action,比如这里的例子:
接下来我们会将定义好的动作的action的这个函数以及所对应的type也就是类型,把它们全部都通过export default的方式导出去。
在外面就可以通过import方式,获取到对应的action的动作。
reducer
同样的我们也会在reducer文件夹里面定义关于用户对应的那些管理员函数,同样会区分于不同的文件
一般来说,我们会定一个管理员的reduce函数我们里面通常要对按说传进来的type进行个区分。因为要用到action里面的type,所以我们需要将action引进来。
在定义的管理员函数当中,我们会初始化状态对象,可能初始化成一个空的对象。
同时接收一个action对象作为参数,这个对象里面有 type 和 payload 的属性。我们在函数里面去根据 type 的值来确定对应的状态值是什么。
比如上面的reducer函数,当type值为成功时,返回的是 payload 值。
combineReducers
这样定义好了对应的管理员函数之后,我们可以通过react-redux中的 combineReducers 这个方法将定好的多个管理员函数全部进行连接起来,并且通过export default这个方法将这些管理员函数都导出去,提供给外面进行使用。
组件中使用 action 和reducer
下面我们来所以说在组建当中是如何使用这些reducer函数以及action
在这里我们要归纳一下所有的action导出的都是统一的dispatch方法,而所有的reduce导出的都是对应的状态对象。所以我们在组件中使用这些状态对象和dispatch方法的时候,我们就需要用到react-redux里面的connect函数了。
我们使用connect函数的时候传入对应的 mapStateToProps 以及 mapDispathToProps 这两个方法
mapStateToProps 这个方法,将返回一个对象。其中里面定义的东西从reducer 里面拿取到的状态对象。这些状态对象通过这个方法以及connect这个方法,将状态对象变成组件的属性对象值。
在这里我们需要强调一点的是:通过connect这个方法,我们已经将状态对象中的属性和组件中的 props 值进行了一个绑定。
非常重要的一点也就是如果状态state对象中的属性值发生了变化,那么将会引起组件中的 props 值的变化。这也就省略了我们去手动进行组件和状态值的映射。这是因为connect的方法,就已经帮我们做了这件事情。
mapDispathToProps同样返回一个对象,它里面定义的是组件中需要的dispatch方法。而这些方法我们可以通过导入对应的action中的dispatch方法进行使用,也就是说我们通过connect这个方法将action中的函数导入到组件当中进行使用。
这里就是组建中action的注入。
这样我们就可以在组件中的prop中直接进行使用,因为这些方法或者state 对象都已经挂载到了组件的 Props 属性上。