Redux
概念
是什么
- redux是一个专门用于做状态管理的JS库(不是react插件库)。
- 它可以用在react, angular, vue等项目中, 但基本与react配合使用。vue有vuex可以使用。
- 作用: 集中式管理react应用中多个组件共享的状态。
做什么
- 某个组件的状态,需要让其他组件可以随时拿到(共享)。
- 一个组件需要改变另一个组件的状态(通信)。
- 总体原则:能不用就不用, 如果不用比较吃力才考虑使用。
工作原理
三个核心概念
action
动作的对象,包含2个属性
- type:标识属性, 值为字符串, 唯一, 必要属性
- data:数据属性, 值类型任意, 可选属性
例子:{ type: 'ADD_STUDENT',data:{name: 'tom',age:18} }
import React,{ Component } from "react";
import store from "./redux/store"; // 引入store
class App extends Component {
// 组件挂载完毕后监听store的data值
componentDidMount() {
store.subscribe(()=>{
this.setState({})
})
}
// 调用action方法改变数据
handel = () => {
const value = 1
store.dispatch({type:'add',data:value})
}
render() {
return (
<>
<div>{store.getState()}</div>
<button onClick={this.handel}>+</button>
</>
)
}
}
reducer
- 用于初始化状态、加工状态。
- 本质上是一个函数。
- 有两个参数:
- preState:之前的状态
- action:动作对象
const initNum = 0
export default function myReducer(preState = initNum, action) {
const {type,data} = action
switch (type) {
case 'add':
return preState + data
case 'reduce':
return preState - data
default:
return preState
}
}
store
将state、action、reducer联系在一起的对象
- 引入创建
store
的方法import {legacy_createStore as createStore} from 'redux'
- 引入创建好的
reducer.js
文件import reducer from './reducers'
- 使用并导出
export default createStore(reducer)
异步action
异步action,就是指action的值为函数。
- redux默认是不能进行异步处理的
- 某些时候应用中需要在redux 中执行异步任务(ajax, 定时器)
利用中间插件
需要配合中间插件,不然会报错。
-
下载
yarn add redux-thunk
-
引入
import thunk from 'redux-thunk'
-
引入redux的中间件方法
import {applyMiddleware} from 'redux'
-
暴露store中使用
export default createStore(myReducer, applyMiddleware(thunk))
-
监听变化
和通过
this.setState
一样,需要通过subscribe
监听redux的内容的变化,否则不起效果。而这个检测可以写在入口文件index.js
中。import store from '@/redux/store' store.subscribe(() => { ReactDom.render(<App />, doucment.getElementById('root')) })
React-Redux
两大类组件
- UI组件
- 只负责 UI 的呈现,不带有任何业务逻辑
- 通过props接收数据(一般数据和函数)
- 不使用任何 Redux 的 API
- 一般保存在components文件夹下
- 容器组件
- 负责管理数据和业务逻辑,不负责UI的呈现
- 使用 Redux 的 API
- 一般保存在containers文件夹下
引用
-
下载
yarn add react-redux
-
引入ui组件
在
containers/ui/index.jsx
文件中引入import ui from '@/components/ui'
-
引入redux
import store from '@/redux/store'
这种引入方式是错误的,运行之后页面会报错,显示找不到store,需要在
App.js
中引入。import Ui from '@/containers/ui' import store from '@/redux/store' render() { return ( <div> <Ui store={store}></Ui> </div> ) }
-
引入connect用于连接ui组件与redux
在
containers/ui/index.jsx
文件中引入import {connect} from 'react-redux' export default connect()(ui) // 使用connect()()创建一个容器组件并暴露出去
使用
a函数的返回值的对象中key作为传递给ui组件props的key,value作为传递ui组件props的value——状态。
通过 store.getState()
可以获取到初始值,但是这么写有问题。目前我们在容器组件内,一开始已经引入好了,函数a是通过 react-redux
调用的,可以直接使用 state
接收参数。
function a(state) {
return {count: state}
}
b函数的返回值的对象中key作为传递给ui组件props的key,value作为传递ui组件props的value——操作状态的方法。
function b(dispatch) {
return {
add: (number) => dispatch({type:'add', data:number})
reduce: (number) => dispatch({type:'reduce', data:number})
}
}
把两个函数传过去。此时在ui组件中可以通过 this.props
获取到相应的数据。
export default connect(a, b)(ui)
注意:
函数不能这么取名,真正项目中应该取为
mapStateToProps
和mapDispatchToProps
。
优化
简写connect
- 把state函数变为一个箭头函数,直接写在括号内。
- 把dispatch函数写成箭头函数,直接写在括号内。然后把函数改为一个对象的形式,直接调用action函数,根据函数名直接去调用原来的功能函数。
import { createAdd } from '@/redux/action'
export default connect(
state => ({count:state}),
{
add: createAdd
}
)(ui)
简化provider
- 容器组件会监听内容的变化,因此不再需要
subscribe
方法的检测,返回index.js
文件中删除即可。 - 容器组件曾通过
store={store}
传递,但是如果容器组件一多,就要一遍一遍地写,很繁琐。解决方法:找到App.js
的上一层,也就是index.js
文件,在这里引入。import store from '@/redux/store' import Provider from 'react-redux' ReactDom.render( <Provider store={store}> <App/> </Provider>, document.getElementById('root'))
组合组件
把ui组件和容器组件结合起来在一个文件中。
共享状态
创建多个 redux 组件时,要在store.js文件中引入暴露,但是不能同时多个暴露,会报错,此时需要用到 redux
中的 combineReducers
方法,传入的对象是redux保存的总状态对象。
import {createStore, applyMiddleware, combineReducers} from 'redux'
import thunk from 'react-thunk' // 异步action
const allReducer = combineReducers({
count: countReducer,
person: personReducer
})
export default createStore(allReducer, applyMiddleware(thunk))