文章目录
Redux 使用
为什么要在React中使用Redux?
我们知道,react的核心是组件,而组件中有两个重要的属性:
- props,父子组件通信的桥梁
- state,组件内部的状态管理,仅组件内部可访问和修改。
React没有数据回溯的能力,主张单向数据流,所以很多时候,我们发现无法让两个组件进行通信(或者说共享状态),而通过层级传递的方法有比较难以接受。所以,我们需要一种机制,能把所有的state集中到组件顶部,能够灵活的将所有state各取所需的分发给所有的组件,这就是Redux。
Redux工作原理
正如上面的图片展示一样,redux的数据流是单向的。
我们将状态统一存储在store里面,而我们可以改变state的唯一方法就是去派发(dispatch)一个方法(action),这个action会被相应的reducer进行处理,返回新的state。
那么我们的组件如何知道state更新了呢?
我们可以使用订阅(subscribe)的形式,来刷新视图,当dispatch一个action导致状态更新时,自动去发布通知,执行render。
Reducer是啥?
reducer是一个纯函数,接受旧的state和action,返回新的state。保持它的纯净很关键,永远不要在里面做类似如下的操作:
- 修改入参
- 副作用操作,如异步请求,路由跳转
- 调用非纯函数,如 Math.random(),new Date
下面来看下具体的使用
Redux基本使用
- 安装redux
npm i redux -S
- 创建store
import { createStore } from 'redux'
// reducer 创建
function counterReducer(state = 0, action) {
console.log(state) // 可以查看状态的变更
switch (action.type) {
case 'ADD':
return state + 1
break;
case 'minus':
return state - 1
default:
return state
break;
}
}
const store = createStore(counterReducer)
export default store
- 引入,获取状态,操作
// 1. 引入
import store from '../store/reduxStore'
class ReduxPage extends Component {
state = {}
// 3. 状态变更
add = () =>{
store.dispatch({type:'ADD'})
}
render() {
return (
<div>
// 2. 状态获取
<p>{store.getState()}</p>
<button onClick={this.add}>计数</button>
</div>
);
}
}
我们可以在页面中打印store,发现它是一个对象,包括几个方法,其中就有getState、dispatch、subscribe等方法。
此时,在页面点击按钮进行计数加一,控制台中我们发现状态变了,但是页面内容没有发生变化,这是因为我们变更了state,但是没有通知页面刷新。
4. 订阅变更
页面没有显示最新的count,主要是因为页面没有重新render,所以我们可以有一下几种方式来实现页面的强制刷新
- dispatch后,手动
this.setState({})
this.forceUpdate()
- 订阅通知
前两种应该很熟悉了,我们来实现最后一种
// 入口文件 index.js
import store from './store/reduxStore'
store.subscribe(()=>{
ReactDOM.render(<App />, document.getElementById('root'));
})
总结
- createStore 创建store
- reducer 初始化、修改状态函数
- getState 获取状态值
- dispatch 提交更新
- subscribe 变更订阅
下篇文章,我们手动实现一个简单的Redux,现在先来看下React-Redux,它使我们能够更好的使用redux。
React-Redux使用
安装
npm i react-redux -S
react-redux基本使用
- 创建store,跟上面的栗子一致
- 引入,并在顶层导入
// 入口index.js
import { Provider } from 'react-redux'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
, document.getElementById('root')
);
内部原理实际上是使用了Context.Provider
- 在组件中使用
//1. 导入辅助函数connect
import { connect } from 'react-redux'
class ReactReduxPage extends Component {
state = {}
render() {
// 3. 直接从props中拿到想要的状态和函数
const { count, add, minus } = this.props
return (
<div>
<p>{count}</p>
<button onClick={add}>加</button>
<button onClick={minus}>减</button>
</div>
);
}
}
// 2. connect接受当前组件,将store映射到组件的props
export default connect(
//mapStateToProps
state => ({ count: state }),
//mapDispatchToProps
{
add: () => ({ type: 'ADD' }),
minus: () => ({ type: 'MINUS' })
}
)(ReactReduxPage);
connect返回一个HOC,接受当前组件,返回一个组件,它里面的实际工作是接受两个参数,第一个是mapStateToProps,定义了将状态映射为props的规则,另一个mapDispatchToProps定义了将action映射为具体props中函数的规则,它可以是一个对象或函数。
Redux中的异步
Redux只是个纯粹的状态管理器,默认只⽀持同步,实现异步任务
⽐如延迟,⽹络请求,需要中间件的⽀持。
redux-thunk是可以用来处理异步操作的一个中间件。
现在加入我们不用redux,我们来模拟一个异步的操作,添加一个延迟一秒后新增计数的按钮。
<button onClick={this.asyncAdd}>异步加</button>
我们会定义一个函数,然后异步结束后手动调用dispatch,像这样:
asyncAdd =()=>{
setTimeout(()=>{
store.dispatch({type:"ADD"})
},1000)
}
现在我们看下redux-thunk是如何更好的解决的。
Redux-thunk
- 安装redux-thunk
npm i redux-thunk -S
- 加载中间件
import { counterReducer } from '../reducer/counterReducer'
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import logger from 'redux-logger'
const store = createStore(counterReducer, applyMiddleware(logger, thunk))
export default store
我们使用中间件的形式,对redux的dispatch进行了增强,可以处理异步操作,其实是对action进行了拦截处理,最后将执行权交还给redux
3. 异步action 返回一个函数,不是对象
// 将action抽离为单独的文件
export const add = () =>({type:'ADD'})
export const minus = () =>({type:'MINUS'})
// 异步action,最终返回的不是一个对象,而是一个函数,以dispatch入参,执行完异步操作后,载进行redux的dispatch
export const asyncAdd = payload => {
return dispatch => {
setTimeout(() => {
dispatch({ type: 'ADD' })
}, 1000)
}
}
thunk会拦截action,对普通的同步action放行,对异步函数式的action,直接执行函数。
5. 引入,操作
import React, { Component } from 'react'
import { connect } from 'react-redux'
// 1. 将action抽离为单独的文件
import { add, minus, asyncAdd } from '../action/counterAction'
class ReactReduxPage extends Component {
state = {}
render() {
// 3. 这里不变
const { count, add, minus, asyncAdd } = this.props
return (
<div>
<p>{count}</p>
<button onClick={add}>加</button>
<button onClick={asyncAdd}>异步加</button>
<button onClick={minus}>减</button>
</div>
);
}
}
export default connect(
state => ({ count: state }),
//2. 进行映射
{
add,
minus,
asyncAdd
}
)(ReactReduxPage);