redux及react-redux基础使用
redux应该是react开发中最为大家熟知的一个进行统一状态管理的库了。不过值得一提的是,redux并非必须配合react去使用,它是可以单独使用去进行状态管理的,或者配合vue等框架去使用。
基本的redux
整个流程可以理解为在组件中通过dispatch一个携带要修改的数据的类型和参数的action交给store,然后store交给reducer去执行。reducer将数据处理后再更新store中的数据,组件就可以通过getState()获取共享的状态值。
包括一下几个主要部分的定义:
1、reducer
通常可以被命名为xxx_reducer.js。文件export一个方法,通过该方法对state进行修改,方法接收两个参数:之前的state值和action对象。action对象包含两部分,一是操作类型,二是操作携带的参数。通常会给第一个值指定一个默认值,作为该state的初始值。
const countRecucer = (preState = 0, action)=>{
const { type, data } = action;
switch(type){
case "increment":
return preState + data;
default:
return preState;
}
}
这里要注意,reducer要是一个纯函数。所谓纯函数是这样的一类函数:只要是同样的输入,必定会得到同样的输出,有以下几个特征:
- 不得改写参数。
- 不会产生副作用,如网络请求、输入和输出设备等。
- 不能调用
Date.now()
或者Math.random()
等不纯的方法。
这里如果违背reducer是一个纯函数的要求,输出的对象仍是输入的preState,比如往数组中push值,往对象中增加新key等等,redux是无法更新视图的。
另外值得一提的是,在组件中调用store.getState()获取值的时候,redux会自动帮我们调reducer去做初始化,会发送一个如下的action对象进行初始化。
{type: '@@redux/INIT0.g.h.f.l'}
2、store.js
store是整个redux的核心,组件中正是使用这个文件export出去的store对象。这里需要借助redux提供的createStore()
方法去创建该对象。
import { createStore } from 'redux'
import countReducer from './count_reducer'
export default createStore(countReducer)
3、在组件中使用
剩下的就是在组件中使用了,通过以下代码就能在组件中dispatch一个action,从而修改共享的状态值。
store.dispatch({type:'xxx',data:xxx})
组件中也可以再通过store.getState()
获取存在redux中的共享状态值。
补充
以上就是一个最简单的redux使用的流程了,但也需要再注意以下几个问题:
状态修改,组件更新
仅仅修改state的值是不能再页面上体现出来的。redux只是帮我们做了数据的存取修改,并没有帮我们调用render。
所以需要我们手动触发render,可以在钩子函数中做监听,如下:
componentDidMount(){
store.subscribe(()=>{
this.setState();
})
}
或者在全局的index.js中去刷新所有组件:
store.subscribe(()=>{
ReactDOM.render(<App />,document.getElementById('root'))
})
ActionCreator
可以通过一个方法,返回一个对象的方式去创建action,而不是直接写action对象,这样也方便接下来实现异步action。
export const createIncrementAction = (data)=>{
return {type:'increment', data}
}
异步action
通过方法创建action,不返回一个对象而是返回一个方法,就能实现异步action,在action中执行一些异步操作。
export const createIncrementAsyncAction = (data, time)=>{
return (dispatch)=>{
setTimeout(()=>{
dispatch(createIncrementAction(data))
}, time)
}
}
注意,如果使用方法作为返回值的方式去创建action,需要在store.js中用一个中间件配合:
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import countReducer from './count_reducer'
export default createStore(countReducer, applyMiddleware(thunk))
以上这两种借用方法创建action的方式,可以在组件中这样使用:
store.dispatch(createIncrementAction(data))
store.dispatch(createIncrementAsyncAction(data,1000))
常量 constant.js
因为在reducer和action中都要写同一个action的type,故可以单独提一个文件,维护常量供二者使用,防止修改手误,一般放在constant.js
中。
export const INCREMENT = Symbol();
多组状态
我们可能会遇到不同模块的组件共用不同的对象,所以就有了多组组件分别保存自己的state到redux中这种需求。
首选需要明确的是,全局只有一个redux,一个store,故所有的内容都应白存在这一个里。之前只有一个组件,故只有一个reducer,所以创建store第一个参数就直接是这个reducer。但是如果有多个reducer,store.js就需要如下的操作了:
import { createStore, applyMiddleware, combineReducer } from 'redux'
import thunk from 'redux-thunk'
import countReducer from './count_reducer'
import personReducer from './person_reducer'
const allReducer = combineReducer({
count: count_reducer,
person: person_reducer
})
export default createStore(allReducer, applyMiddleware(thunk))
注意,这个时候存储在redux中的store就变成allReducer中这个对象,也就是:
{
count: countReducer中返回的值,
person: personReducer中返回的值
}
在组件中取值,store.getState()
取到的也是这个值。
react-redux
这是一个react提供的组件,配合redux实现状态的管理,能够让我们无需手动触发render()
,它会在state改变之后,自动帮我们更新视图。
react-redux需要我们罢组件分成容器组件和UI组件,它们是父子关系,容器组件才真正跟redux打交道,里面可以随意使用redux的api,但UI组件不能够使用任何redux的api。
容器组件会传给UI组件:1、redux中所保存的状态,2、用于操作状态的方法。以上两者都是通过props传递的。原理图如下:
容器组件
最关键的部分就是容器组件的创建,容器组件的创建使用的是react-redux提供的connect()()
这个方法。这是一个高阶方法,第二个括号中的参数就是UI组件,假设我们有UI组件<Count />
,则使用方式是:
import { connect } from 'react-redux'
export default connect()(Count)
而第一个括号中,传递的是两个方法。分别是将state映射成props的方法和将dispatch映射成props的方法。通过这两个方法,UI组件就能使用redux中存的状态和对状态操作的方法了。
import {connect} from 'react-redux'
import {crateIncrementAction, crateDecrementAction } from '../redux/count_action'
const mapStateToProps = (state)=>{
return {
count: state
}
}
const mapDispatchToProps = (dispatch)=>{
return {
increment: (value)=>dispatch(createIncrementAction(value)),
decrement: (value)=>dispatch(createDecrementAction(value))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Count)
如上,创建好容器组件后,在UI组件中只需要通过使用this.props就可以使用count这个状态和increment、decrement这两个操作状态的方法去跟redux进行交互了。
而容器组件的使用,可以在使用的地方直接调用,同时传入state就行,假设我们有容器组件Count,则:
import store from '../redux/store'
<Count store={store} />
优化mapDispatchToProps
值得一提的是,mapDispatchToProps这个方法是可以进行优化的。它可以不使用一个方法,转而使用一个对象,有一个映射到生成action的对象就行了,以上提到的mapDispatchToProps可以优化成如下:
import {crateIncrementAction, crateDecrementAction } from '../redux/count_action'
const mapDispatchToProps = {
increment: createIncrementAction,
decrement: createDecrementAction
}
Provider
在使用容器组件时,需要传入store。实际上如果每个容器组件都需要传一遍就会比较麻烦,我们可以使用Provider进行优化:
import Count from './container/Count'
import { Provider } from 'react-redux'
export default class App extends Component {
render() {
return (
<div id="app">
<Provider store={store}>
<Count />
</ Provider>
</div>
);
}
}
这样,在Provider中的每一个容器组件,都不需要再传入store了。
Redux DevTools
可以使用chrome的一个插件来帮助我们在开发中监控redux中的状态。
- 首先需要安装chrome的插件Redux DevTools
- 在项目中npm install redux-devtools-extension
- 在store.js中做如下修改
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import allReducer from './reducers'
import { composeWithDevTools } from 'redux-devtools-extension'
export default createStore(allReducer, composeWithDevTools(applyMiddleware(thunk)))