前言:
工程项目里往往存在复杂的数据流,科学高效的管理这些数据及其流转是前端工程师必须解决的问题。代码里面,数据流管理的好能让组件之间通信变得更加容易,也使得数据更容易维护。一种常见的方式就是使用redux,在react中我们还可以使用react-redux来简化操作。本文先介绍redux,再引出react-redux。读完本文,你应该对redux以及react-redux有了相对全面的了解,并能在项目里面使用。
一、redux
- 什么是redux? 按照官网上的解释:redux 是 js 应用的可预测状态的容器。 其实就是一个全局数据状态管理工具(状态管理机),管理数据流用来做组件通信等。需要注意的是,Redux 和 React 之间没有关系。Redux 支持 React、Angular、Ember、jQuery 甚至纯 JavaScript
当没有使用redux时兄弟组件间传值将很麻烦,代码很复杂冗余。使用redux定义全局单一的数据Store,可以自定义Store里面存放哪些数据,整个数据结构也是自己清楚的。
- redux如何管理数据流?
上图是redux对数据流的管理有一个固定的流程,要理解该过程,首先我们需要明确三个概念:store,action,reducers。
- store:store是保存数据state的地方,你可以把它看成是数据源对数据进行统一管理,整个应用中只能有一个store,所有组件均可通过redux规范与store中的数据进行交互
- action:action 就是 JavaScript 普通对象。并且action 内必须使用一个字符串类型的 type 字段来表示将要执行的动作(规定action对象里面必须有type字段,否则代码将报错)
- reducers: actions发送给store,根据action中的type来改变相应的store内的state,但是如何改变呢?就是通过reducers来改变的,reducers是一个纯函数,并且记住 actions 只是描述了有事情发生了这一事实,并没有描述应用如何更新state,而是有reducers根据action对象内的信息来改变store内的数据
理解这三个名词上面的redux流程图就好理解了: 当组件需要改变store内的数据时,就发送一个action给到store,store收到action后就会调用相应的reducers函数,reducers函数会返回一个新的对象来作为新的数据值回传给store,之后组件再从store里面获取更新后的数据了。
- 结合react,代码说明redux的使用 那么action如何定义,如和定义store,action如何发送给store,reducers是如何更新store内的数据值的,组件内又是如何获取到新的store内的值呢?我们一一道来。
首先我们需要分别定义action,reducers以及store:
//分别用三个文件actions.js,reducers.js,store.js来定义action,reducer,store
//actions.js 前文已经说过,action就是一个普通的对象,其中必须有type字段
const action={
type:'ADD_TODO',
payload:'redux原理'
}
export default action;
//reducers.js 定义一个纯函数,用于处理store内的state
const reducer =(state={},action)=>{
switch(action.type){
case ADD_TODO:
return newstate;
default return state
}
export default reducer;
//store.js store就是整个项目保存数据的地方,并且只能有一个。创建store就是把reducer给它
import { createStore} from "redux";
//把定义的reducer引入进来
import reducer from "./reducer.js";
// 全局就管理一个store
const store = createStore(reducer)
export default store;
//至此三个文件定义完成,那组件如何去更新store内的state呢?
//component.js,定义一个我们的组件
import React, { Component } from 'react';
import store from './store';
import action from './action';
export default class Home extends Component {
componentDidMount(){
//redux需要调用store.subscribe监听store的变化,store.getState用来获取store内的state,
//store.subscribe调用返回的值unsubscribe在页面卸载的时候调用,目的是取消页面对store的监听,防止内存泄漏
let unsubscribe = store.subscribe(() =>
console.log(store.getState())
);
//unsubscribe();
}
change=()=>{
//store.dispatch会向store发送action,store接收到action后就会自动调用reducers,
//reducers根据action中的type执行相应的处理逻辑,并且返回新的state给到store,
//此时store.subscribe()就会被触发,通过store.getState()就可以拿到store内的state值
store.dispatch(action);
//其实就是一下代码
// store.dispatch({
// type: 'ADD_TODO',
// payload: 'redux原理'
//});
}
render() {
return <div className='container'>
<p onClick={this.change}>test</p>
</div>
}
}
以上是一个redux使用的小案例,里面包含了组件如何发送action修改store以及如何从store中获取数据。通过这个案例你应该明白了代码中如何在组件中使用redux管理数据,总结就是一下几点:
- 定义action,reducers,store,这步是基础
- 在组件中引入store,通过store.subscribe()自动监听store内的变化
- 组价中需要改变store是,用store.dispatch(action),执行后reducers会自动执行相应的逻辑修改store
- 组件中获取store,用store.getState()
二、react-redux
-
什么是react-redux? react-redux是一个react插件库,专门用来简化react应用中使用redux。他是从redux封装而来,因此基本原理和redux是一样的,同时存在一些差异。
-
react-redux模型图
React-Redux 将所有组件分成两大类:UI 组件(presentational component)和容器组件(container component)
UI组件
- 只负责 UI 的呈现,不带有任何业务逻辑
- 没有状态(即不使用this.state这个变量)
- 所有数据都由参数(this.props)提供
- 不使用任何 Redux 的 API 容器组件
- 负责管理数据和业务逻辑,不负责 UI 的呈现
- 带有内部状态
- 使用 Redux 的 API
- UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑 React-Redux 规定,所有的 UI 组件都由用户提供,容器组件则是由 React-Redux 自动生成(由connect()函数生成)。也就是说,用户负责视觉层,状态管理则是全部交给它
- react-redux相比于redux简化在哪里? 我们已经知道,组件使用redux与store交互需要引入store,并且要手动调用store.subscript()监听store变化,使用起来比较麻烦。react-redux简化了这些步骤,使数据流管理使用起来更加方便。这些功能的实现得益于四个react-redux的API:provider、connect、mapStateToprops、mapDispatchToProps
- 组件:React-Redux 提供Provider组件,该组件把根组件包含起来,并且provider组件需要传入store,这样一来所有组件都可以得到state数据
//APP组件被包裹后,所有子组件都可以得到state数据
<Provider store={store}>
<App />
</Provider>
- connect:provider传入store,所有子组件都能拿到state,那具体怎么拿呢?就需要用到connect,它用于包装 UI 组件生成容器组件。connect包裹后生成了一个容器组件,connect方法接受两个参数:mapStateToProps和mapDispatchToProps。它们定义了 UI 组件的业务逻辑。前者负责输入逻辑,即将state映射到 UI 组件的参数(props),后者负责输出逻辑,即将用户对 UI 组件的操作映射成 Actio,此时就可以与store交互了。
import { connect } from 'react-redux'
connect(
mapStateToprops,
mapDispatchToProps
)(OurComponent)
- mapStateToProps(): 它是一个函数,建立一个从(外部的)state对象到(UI 组件的)props对象的映射关系,把store的state挂载到组件的props上。mapStateToProps执行后应该返回一个对象,里面的每一个键值对就是一个映射。
const mapStateToProps = (state) => {
return {
todos: "todo value"
}
}
- mapDispatchToProps(): mapDispatchToProps是connect函数的第二个参数,用来建立 UI 组件的参数到store.dispatch方法的映射,把dispatch触发action的函数挂载到props上。它定义了哪些用户的操作应该当作 Action,传给 Store。它可以是一个函数,也可以是一个对象。
//dispatch就是store.dispatch,ownProps是容器组件的props对象
const mapDispatchToProps = (
dispatch,
ownProps
) => {
return {
onClick: () => {
dispatch({
type: 'SET_VISIBILITY_FILTER',
filter: ownProps.filter
});
}
};
}
因此,react-redux通过provider组件做到了store的统一管理,并利用connect包裹组件生成容器组件是的组件与store打通数据流,这样一来就不需要在每一个组件中引入store,并监听store变化,使用更加方便