Redux 和 React 之间没有关系。Redux 支持 React
、Angular
、Ember
、jQuery 甚至纯 JavaScript
。尽管如此,Redux 还是和 React 和 Deku 这类库搭配起来用最好,因为这类库允许你以 state 函数的形式来描述界面,Redux 通过 action 的形式来发起 state 变化。
下面我们来看一个使用 react-redux 完成计数器的案例:
最终实现的效果:点击左边的加号按钮,中间的数字加2(可以自定义参数);点击右边的减号按钮,中间的数字减1;
目录结构:
├── index.jsx //入口文件
├── Count.jsx //显示页面内容的组件
├── Button.jsx //按钮组件
├── store.js //创建一个store,用来集中管理state dispatch action
├── reducer.js //业务逻辑层,对数据进行操作,并返回新的state
├── connect.js //将redux和展示组件做关联,将redux中的属性和方法挂载到组件里
安装依赖:在终端中执行下面的命令,安装 redux 和 react-redux
yarn add redux react-redux -S
store.js文件:
- 用来创建一个 store,集中管理state dispatch action,参数是 reducer。
/* store.js */
import {createStore} from 'redux'
import reducer from './reducer'
//创建store
const store=createStore(reducer)
export default store;
reducer.js文件:
- reducer 是一个纯函数,它接收两个参数 state 和 action;
- 当处理数据时,先把原来的数据拿过来,如果没有数据,会读取
初始值
; - reducer 是业务逻辑层,代码根据业务需求来写,它的作用是根据 action 传过来的内容,对
数据做操作
; - reducer 最终返回一个
新的state
,当 state 被更改后,store.subscribe 方法里的回调函数就会执行,会通知 view 去重新获取 state,做视图的更新;
/* reducer.js */
const defaultState={count:1}
const reducer=(state=defaultState,action)=>{
switch (action.type){
case "inc":
return{ count:state.count+action.num }
case "dec":
return{ count:state.count-1 }
default:
return state;
}
}
export default reducer;
connect.js文件:
- 帮我们把
redux
和展示组件
做关联
,将 store 里的属性和方法挂载到组件里; - connect 就是一个高阶函数,把组件作为参数传进去,组件中就能读到store中的数据;
- connect 的语法格式为:
connect(mapStateToProps?, mapDispatchToProps?)(component)
- 将第一个参数中返回的属性转化为展示组件
props
上的属性; - 将第二个参数中返回的方法转化为展示组件
props
上的方法; - 如果只想获取到方法,在第一个参数传null;
- 将第一个参数中返回的属性转化为展示组件
/* connect.js */
import {connect} from 'react-redux'
const mapState=(state)=>{
return{ count:state.count }
}
const mapDispatch=(dispatch)=>{
return{
increm:(data)=>{ dispatch({type:"inc",num:2}) },
decrem:()=>{ dispatch({type:"dec"}) }
}
}
export default connect(mapState,mapDispatch)
Count.jsx :
- 使用 connect 对当前组件做一个增强,Count 组件里就可以通过 this.props 读取到 store 中的数据;
/* Count.jsx */
import React, { Component } from 'react'
import Button from './Button'
import connect from './connect'
class Count extends Component {
render() {
return (
<div>
<Button type="inc">+</Button>
<span>{this.props.count}</span>
<Button type="dec">-</Button>
</div>
)
}
}
export default connect(Count)
Button.jsx:
- 使用 connect 对当前组件做一个增强,Button 组件里通过 this.props 调用 connect 中的方法;
- connect 中调用 store.dispatch 方法将 action 发送到 reducer 中;
- reducer 接收到 action,对数据进行处理后,返回新的 state;
- 当 state 被更新的时候,store.subscribe 方法会执行,通知 view 去重新获取 state;
- 获取到新的 state 后,会触发 render() 重新渲染页面;
/* Button.jsx */
import React, { Component } from 'react'
import connect from './connect'
class Button extends Component {
clickHandle=()=>{
let {type,increm,decrem} =this.props;
if(type==="inc") increm(2)
else decrem()
}
render() {
return (
<button onClick={this.clickHandle}>
{this.props.children}
</button>
)
}
}
export default connect(Button)
index.js:
- 在父组件最外面包裹
<Provider>
,并提供store
属性,则在后代组件中都可以通过 connect 对组件做增强,读到 store 里的属性和方法;
/* index.js */
import React from 'react'
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux'
import store from './store'
import Count from './Count'
ReactDOM.render(
<Provider store={store}>
<Count/>
</Provider>,
document.getElementById('root')
);
总结:
react-redux 中两个核心的api:
- Provider: 提供store,根据单一store原则 ,一般只会出现在整个应用程序的最顶层。
- connect: 用于连接展示组件和 store。
react-redux 开发思想:
Redux 的 React 绑定库是基于容器组件和展示组件相分离的开发思想。
展示组件 | 容器组件 | |
---|---|---|
作用 | 描述如何展现(骨架、样式) | 描述如何运行(数据获取、状态更新) |
直接使用 Redux | 否 | 是 |
数据来源 | props | 监听 Redux state |
数据修改 | 从 props 调用回调函数 | 向 Redux 派发 actions |
调用方式 | 手动 | 通常由 React Redux 生成 |
大部分的组件都应该是展示型
的,但一般需要少数的几个容器组件把它们和 Redux store 连接起来。技术上讲你可以直接使用 store.subscribe() 来编写容器组件
。但不建议这么做的原因是无法使用 React Redux 带来的性能优化。也因此,不要手写容器组件,而使用 React Redux 的 connect()
方法来生成。