完整代码地址 https://gitee.com/xiaozhidayu/my-react
redux
redux 理解
1、学习文档
1).英文文档: https://redux.js.org/
2).中文文档: http://www.redux.org.cn/
3).Github: https://github.com/reactjs/redux
2、redux 是什么?
1). redux 是一个独立专门用于做状态管理的JS 库 (不是react 的插件库)
2).他可以用在react angular vue 等项目中,但基本与react 配合使用
3). 作用:集中管理react应用中多个组件共享的状态
3、redux 工作流程
4、什么情况下需要使用redux
1)、总体原则,能不用就不用,如果不用 项目写起来比较吃力才用(!!!其实在实际项目开发中基本都需要用)
2)、某个组件的状态需要共享
3)、某个状态需要在任何地方都可以拿到
4)、一个组件需要改变全局状态
5)、一个组件需要改变另外一个组件的状态
redux 核心API
1、createStore()
①作用:创建包含指定reducer 的 store 对象
② 编码:
import {createStore} from 'redux'
import counter from './reducers/counter'
const store = createStore(counter)
2、store 对象
① 作用:redux 库最核心的管理对象
② 它内部维护着 : state 、 reducer
③ 核心方法:getState() 、dispatch(action)、subscribe(listener)
④ 编码:store.getState()、 store.dispatch({type:'INCREMENT', number})、store.subscribe(render)
3、applyMiddleware
① 应用上基于redux 的中间件(插件库)
② 编码 :
import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk' // redux异步中间件
const store = createStore(
counter,
applyMiddleware(thunk) // 应用上异步中间件
)
4、combineReducers()
① 作用:合并多个reducer 函数
② 编码:
export default combineReducers({
user,
chatUser,
chat
})
redux 的三个核心概念
1、action
①标识要执行行为的对象
②包含两个方面的属性
a、type:标识属性,值为字符串,唯一,必要属性
b、xxx :数据属性,值类型任意,可选属性 (可统一xxx 为 data,方便统一取数据)
③ 例子 :
const action = {
type: 'INCREMENT',
data: 2
}
④ Action Creator(创建Action的工厂函数)
const increment = (number) => ({type: 'INCREMENT', data: number})
2、reducer
① 根据老的state 和 action ,产生新的state 的 纯函数
② 例子:
export default function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + action.data
case 'DECREMENT':
return state - action.data
default:
return state
}
}
③注意:
a、返回一个新的状态
b、不要修改原来的状态
3、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时, 自动调用
实践出真知 应用demo
// import React from 'react'
import React, { Component } from 'react'
import { INCREMENT, DECREMENT } from '../redux/action-types'
import * as actions from '../redux/actions.js'
import '../App.css'
class App extends Component {
state = {
count: 0,
}
increment = () => {
//1、得到选择增加数量
const number = this.select.value * 1
//2、得到原本的count 状态 并计算新的count
// const count = this.state.count + number
//3、更新状态
// this.setState({ count })
//2、调用store 的方法更新状态
this.props.store.dispatch(actions.increment(number))
}
decrement = () => {
//1、得到选择增加数量
const number = this.select.value * 1
//2、得到原本的count 状态 并计算新的count
// const count = this.state.count - number
//3、更新状态
// this.setState({ count })
//2、调用store 的方法更新状态
this.props.store.dispatch(actions.decrement(number))
}
incrementIfOdd = () => {
//1、得到选择增加数量
const number = this.select.value * 1
//2、得到原本的count 状态
// const count = this.state.count
const count = this.props.store.getState()
//3、判断 满足条件才更新状态
if (count % 2 == 1) {
// this.setState({ count: count + number })
this.props.store.dispatch(actions.increment(number))
}
}
incrementAsync = () => {
//1、得到选择增加数量
const number = this.select.value * 1
//2、得到原本的count 状态 并计算新的count
// const count = this.state.count
//3、启动延时定时器 更新状态
setTimeout(() => {
// this.setState({ count: count + number })
this.props.store.dispatch(actions.increment(number))
}, 1000)
}
render() {
// const { count } = this.state
const count = this.props.store.getState()
return (
<div className="App">
<p>click {count} times</p>
<div>
<select ref={(select) => (this.select = select)}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>increment if odd</button>
<button onClick={this.incrementAsync}>increment async</button>
</div>
</div>
)
}
}
// function App() {}
export default App
其他功能由 如下几个文件 分工完成
react-redux
理解
redux 存在一些问题,因此应运而生了 react-redux
- redux与react组件的代码耦合度太高
- 编码不够简洁
1) 一个react插件库
2) 专门用来简化react应用中使用redux
react-redux 将所有组件分为两大类
1) UI组件
a. 只负责 UI 的呈现,不带有任何业务逻辑
b. 通过props接收数据(一般数据和函数)
c. 不使用任何 Redux 的 API
d. 一般保存在components文件夹下
2) 容器组件
a. 负责管理数据和业务逻辑,不负责UI的呈现
b. 使用 Redux 的 API
c. 一般保存在containers文件夹下
相关API
1) Provider
让所有组件都可以得到state数据
<Provider store={store}>
<App />
</Provider>
2) connect() :用于包装 UI 组件生成容器组件
import { connect } from 'react-redux'
connect(
mapStateToprops,
mapDispatchToProps
)(Counter)
3) mapStateToprops(): 将外部的数据(即state对象)转换为UI组件的标签属性
const mapStateToprops = function (state) {
return {
value: state
}
}
4) mapDispatchToProps()
将分发action的函数转换为UI组件的标签属性
简洁语法可以直接指定为actions对象或包含多个action方法的对象
应用demo
模拟了项目中的组件封装,UI 组件 和 容器组件分别放在 component 和 containers 文件夹下,主要代码如下:
// import React from 'react'
import React, { Component } from 'react'
import PropTypes from 'prop-types'
// import { connect } from 'react-redux'
// import { INCREMENT, DECREMENT } from '../redux/action-types'
// import { increment, decrement } from '../redux/actions.js'
import '../App.css'
export default class Counter extends Component {
static propTypes = {
count: PropTypes.number.isRequired,
increment: PropTypes.func.isRequired,
decrement: PropTypes.func.isRequired,
}
// state = {
// count: 0,
// }
increment = () => {
//1、得到选择增加数量
const number = this.select.value * 1
//2、得到原本的count 状态 并计算新的count
// const count = this.state.count + number
//3、更新状态
// this.setState({ count })
//2、调用store 的方法更新状态
// this.props.store.dispatch(actions.increment(number))
this.props.increment(number)
}
decrement = () => {
//1、得到选择增加数量
const number = this.select.value * 1
//2、得到原本的count 状态 并计算新的count
// const count = this.state.count - number
//3、更新状态
// this.setState({ count })
//2、调用store 的方法更新状态
// this.props.store.dispatch(actions.decrement(number))
this.props.decrement(number)
}
incrementIfOdd = () => {
//1、得到选择增加数量
const number = this.select.value * 1
//2、得到原本的count 状态
// const count = this.state.count
// const count = this.props.store.getState()
const count = this.props.count
//3、判断 满足条件才更新状态
if (count % 2 == 1) {
// this.setState({ count: count + number })
// this.props.store.dispatch(actions.increment(number))
this.props.increment(number)
}
}
incrementAsync = () => {
//1、得到选择增加数量
const number = this.select.value * 1
//2、得到原本的count 状态 并计算新的count
// const count = this.state.count
//3、启动延时定时器 更新状态
setTimeout(() => {
// this.setState({ count: count + number })
// this.props.store.dispatch(actions.increment(number))
this.props.increment(number)
}, 1000)
}
render() {
const { count } = this.props
// const count = this.props.store.getState()
return (
<div className="App">
<p>click {count} times</p>
<div>
<select ref={(select) => (this.select = select)}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>increment if odd</button>
<button onClick={this.incrementAsync}>increment async</button>
</div>
</div>
)
}
}
// function App() {}
// export default connect((state) => ({ count: state }), { increment, decrement })(
// App,
// )
容器组件
import React from 'react'
import { connect } from 'react-redux'
import { increment, decrement } from '../redux/actions'
import Counter from '../componments/Counter'
export default connect((state) => ({ count: state }), { increment, decrement })(
Counter,
)
最后修改index.js 中引入的入口组件即可
redux 异步编程
1、下载redux 插件(异步中间件)
npm install --save redux-thunk
2、代码中引入
3、将异步的操作放在action中,action 返回的是一个函数,在特定的时间才触发对应的事件
action 中 代码 定时器模拟异步延时:
export const incrementAsync = (number) => {
return dispatch => {
// 1s 之后才去分发一个增加的action
setTimeout(() => {
dispatch(increment(number))
}, 1000)
}
}
相关重要知识 纯函数& 高阶函数
1、纯函数
1) 一类特别的函数: 只要是同样的输入,必定得到同样的输出
2) 必须遵守以下一些约束
a. 不得改写参数
b. 不能调用系统 I/O 的API
c. 能调用Date.now()或者Math.random()等不纯的方法
3) reducer函数必须是一个纯函数
2、高阶函数
1) 一类特别的函数: 只要是同样的输入,必定得到同样的输出
2) 必须遵守以下一些约束
a. 不得改写参数
b. 不能调用系统 I/O 的API
c. 能调用Date.now()或者Math.random()等不纯的方法
3) reducer函数必须是一个纯函数