1. redux 是什么?
- redux 是一个独立专门用于作状态管理的JS库(不是react插件)
- 它可以用于react、angular、vue等项目中,但基本于react配合使用
- 作用:集中式管理react应用中多个组件共享的状态
2. 使用场景
- 总体原则:能不使用就不用,如果不用比较吃力才考虑使用
- 某个组件的状态需要共享
- 某个状态需要在任意位置都可以拿到
- 一个组件需要改变全局状态
- 一个组件需要改变另一个组件的状态
3. redux 的核心API
3.1. createrStore()
- 作用:创建包含指定reducer的store对象
import {createrSotre} from 'redux'
import counter from './reducers/counter'
const store = createrStore(counter)
3.2. store 对象
- 作用:redux 库最核心的管理对象
- 它内部维护着:
-
- state 存放数据
-
- reducer是根据旧状态产生一个新状态的函数
- 核心方法
-
- getState() 获取状态显示
-
- dispatch(action) 分发事件
-
- subscribe(listener) 订阅监听,更新/重新渲染组件
store.getState()
store.dispatch({type: 'INCREMENT', number})
store.subscribe(render)
3.3. applyMiddleware()
- 作用:应用上基于redux的中间件
import {createStore, applyMIddleware} from 'redux'
import thunk from 'redux-thunk' // redux异步中间件
const store = createStore(
counter,
applyMiddleware(thunk) // 应用上异步中间件
)
4. redux 的三个核心概念
4.1. action
唯一改变state的方法就是触发action,action是一个用于描述以发生事件的普通对象 记住,action只是描述了
有事情发生了这一事实,并没有描述如何更新state
- 标识要执行行为的对象
- 包含2个方面的属性:
-
- type:标识属性,字符串,唯一,必要属性
-
- data:数据属性,任意值类型,可选属性
const action = {type: 'INCREMENT', data: 2}
- action creator(创建action工厂函数,只是简单的返回一个action)
const increment = (number) => ({type: 'INCREMENT', data: number})
4.2. reducer
为了描述action如何改变state,你需要编写reducer Reducer 只是一些纯函数,它接收先前的state和action并返回新的state
- 根据旧的state和action,产生新的state的纯函数
export default function counter(state=0, action) {
// 根据实际情况state要有同等数据类型的默认值
switch(action.type) {
case 'INCREMENT':
return state + action.data
case 'DECREMENT':
return state - action.data
default: // 一般都要写default
return state
}
}
注:返回一个新的state,必要修改旧的state
4.3. store
整个应用的state被存储在一颗object tree中,并且这个object tree只存在于唯一一个store中
- 作用:将state、action、reducer联系在一起的对象
import {createStore} from 'redux'
import reducer from './reducers
const store = createStore(reducer)
- 此对象的功能:
getState() // 获取state
dispatch(action) // 分发action,触发reducer调用,生成新的state
subscribe(listener) // 注册监听,当产生新的state时自动调用
5. 使用redux编写应用
5.1. 目录结构
redux
--action-types.js 包含所有action type的常量字符串
--actions.js 包含所有的 action creator
--reducers.js 包含n个reducer函数的模块
--store.js 生成store对象
index.js 程序入口
- action-types.js
// 包含所有的action type的常量字符串
export const INCREMENT = 'INCREMENT'
export const DECREMENT = 'DECREMENT
- actions.js
// 包含所有的 action creator
import {INCREMENT,DECREMENT} from './action-types'
// 增加
export const increment = (number) => ({type: INCREMENT, data: number})
// 减少
export const decrement = (number) => ({type: DECREMENT, data: number})
- reducer.js
// 包含n个reducer函数的模块
import{INCREMENT, DECREMENT} from './action-types.js'
export function counter (state=0, action) {
switch(action.type) {
case INCREMENT:
return state + action.data
case DECREMENT:
return state -action.data
default:
return state
}
}
- store.js
import {createStore} from 'redux'
import {counter} from './reducers'
// 生成一个store对象
const store = createStore(counter)
export default store
- index.js
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import store from './redux/store'
function render() {
ReactDOM.render(<App store={store) />,
document.getElementById('root'))
}
// 初始化渲染
render()
// 订阅监听,state状态改变,就会自动进行重绘
store.subscribe(render)
- 在组件中使用
import React from 'react'
import * as actions from './redux/actions'
class App extends React.Component {
constructor(props) {
super(props)
}
handleAdd = () => {
const currentNumber = this.select.value*1
this.props.store.dispatch(actions.increment(currentNumber))
}
handleOdd = () => {
const count = this.props.store.getState()
if(count%2 === 1) {
const currentNumber = this.select.value*1
this.props.store.dispatch(actions.increment(currentNumber))
}
}
handleAsync = () => {
const currentNumber = this.select.value * 1
setTimeout(() => {
this.props.store.dispatch(actions.increment(currentNumber))
}, 1000)
}
render () {
// 获取 state
const count = this.props.store.getState()
return (<div>
<p>click {count} times</p>
<select ref={select => this.select = select}>
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.handleAdd}>+</button>
<button onClick={this.handleDecrement}>-</button>
<button onClick={this.handleIfOdd}>increment if odd</button>
<button onClick={this.handleAsync}>increment async</button>
</div>)
}
}
export default App
5.2 问题
- redux于react组件代码耦合度太高
- 编码不够简洁
6. react-redux
一个react插件库
专门用来简化react应用中使用redux
6.1. react-redux 将所有组件分为两大类
- UI组件
-
- 只负责UI的呈现,不带有任何业务逻辑
-
- 通过props接收数据
-
- 不使用任何redux的API
-
- 一般保存在components文件夹下
- 容器组件
-
- 负责管理数据和业务逻辑,不负责UI
-
- 使用redux的API
-
- 一般保存在containers文件夹下
6.2. 相关 API
- Provider
// 所有组件都可以得到state数据
<Provider store={store}>
<App />
</Provider>
- connect()
// 将UI组件生成容器组件,将组件和redux相关联
import {connect} from 'react-redux'
import Counter from '../components/counter' // UI
connect(
mapStateToProps, // 将状态映射为属性
mapDispatchToProps // 将方法映射为属性
)(Counter)
7. 使用react-redux
- 下载依赖包
npm i --save react-redux
- 目录结构
// 增加
components
--counter.js // UI
container
--app.js // 容器组件
- counter.js
// UI
import React from 'react'
import PropTypes from 'prop-types'
export default class Counter extends React.Component {
// constructor(props) {
// super(props)
// this.state = {}
// }
static propTypes = {
count: PropTypes.number.isRequired,
increment: PropTypes.func.isRequired,
decrement: PropTypes.func.isRequired
}
handleAdd = () => {
const currentNumber = this.select.value * 1
this.props.increment(currentNumber)
}
handleDecrement = () => {
const currentNumber = this.select.value * 1
this.props.decrement(currentNumber)
}
handleIfOdd = () => {
// 获取
const { count } = this.props
if (count % 2 === 1) {
const currentNumber = this.select.value * 1
// 改变
this.props.increment(currentNumber)
}
}
handleAsync = () => {
const currentNumber = this.select.value * 1
setTimeout(() => {
this.props.increment(currentNumber)
}, 1000)
}
render () {
this.props.store.getState()
const { count } = this.props
return (<div>
<p>click {count} times</p>
<select ref={select => this.select = select}>
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.handleAdd}>+</button>
<button onClick={this.handleDecrement}>-</button>
<button onClick={this.handleIfOdd}>increment if odd</button>
<button onClick={this.handleAsync}>increment async</button>
</div>)
}
}
- app.js
import React from 'react'
import {connect} from 'react-redux'
import {increment, decrement} from '../redux/actions'
import Counter from '../components/counter'
export default connect(
state => ({count: state}),
{increment, decrement}
)(Counter)
- index.js
import React from 'react'
import ReactDOM from 'react-dom'
import {Provider} from 'react-redux'
import App from './containers/app'
import store from './redux/store'
ReactDOM.render(<Provider store={store}>
<App />
</Provider>, document.getElementById('root'))
- 问题
- redux 默认泵惊醒异步处理
- 应用中需要在redux中执行异步任务
8. redux 异步编程
8.1. 下载异步插件
npm i --save redux-thunk
8.2. 在store.js中使用
import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
import {counter} from './reducers'
const store = createStore(
counter,
applyMiddleware(thunk)
)
export default store
8.3. 在 action.js中使用异步
/***
* 包含所有的 action creator
* 同步的 action 返回一个对象
* 异步的 action 返回一个函数
* 默认只能返回一个对象,加上异步中间件 applyMiddleware(thunk) 才能返回一个函数
*/
import { INCREMENT, DECREMENT } from './action-types'
// 增加
export const increment = (number) => ({ type: INCREMENT, data: number })
// 减少
export const decrement = (number) => ({ type: DECREMENT, data: number })
// 异步action
export const incrementAsync = (number) => {
return dispatch => {
setTimeout(() => {
dispatch(increment(number))
}, 1000)
}
}
8.4. counter.js
handleAsync = () => {
const currentNumber = this.select.value * 1
// 异步中间件 applyMiddleware(thunk)
this.props.incrementAsync(currentNumber)
}
#### 8.5. app.js
```javascript
import React from 'react'
import { connect } from 'react-redux'
// incrementAsync 异步事件
import { increment, decrement, incrementAsync } from '../redux/actions'
import Counter from '../components/counter' // UI
export default connect(
state => ({ count: state }),
{ increment, decrement, incrementAsync }
)(Counter)
9. redux 调试工具
9.1. chrome浏览器插件
- redux-dectools215_1.crx
9.2. 下载依赖包
- npm i --save-dev redux-devtools-extension
9.3. 在store.js中使用
// 引入一个用来创建store对象的一个函数
import { createStore, applyMiddleware } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
import thunk from 'redux-thunk'
import { counter } from './reducers'
// 生成一个store对象
// 内部会第一次调用reducer函数得到初始state
const store = createStore(
counter,
composeWithDevTools(applyMiddleware(thunk)) // 应用上异步中间件
)
export default store
特别提醒
- redux 对于初学者有些难度,需要多看几遍
- 有没看懂的地方欢迎讨论