掌握redux
redux其实是JavaScript应用的状态容器, 它保证程序行为一致性且易于测试.
安装
npm install redux --save
流程
- 需要一个store来存储数据
- store里的reducer初始化state并定义state修改规则
- 通过dispatch一个action来提交对数据的修改
- action提交到reducer函数里, 根据传入的action的type, 返回新的state
// 新建一个store文件夹,里面添加index.js
import {createStore} from 'redux';
const counterReducer = function(state=1, action) {
switch(action.type){
case 'add':
return state+1;
case 'minus':
return state-1;
default:
return state;
}
}
const store = createStore(counterReducer)
export default store
// ----------------------------------------------------
// 需要使用时,在组件中引入store
import store from '../store'
// 如何获得store里的值呢?
{store.getState()} // => 1
// 想要修改store里的值怎么做呢?
<button onClick={()=>store.dispatch({type:'add'})}>+</button>
<button onClick={()=>store.dispatch({type:'add'})}>+</button>
// 然后需要重新渲染组件才行,这有很多方法, 常用的是下面一种
componentDidMount(){
store.subscribe(()=>{
this.forceUpdate();// 强制刷新
})
}
react-redux
每次都重新调用render和getState太low, 想用更react的方式来写, 需要react-redux
npm install react-redux --save
提供了两个api
- Provider为后代组件提供store
- connect为组件提供数据和变更方法
// 只需在根app上引入store, 用Provider包裹根组件
import store from './store'
import {Provider} from 'react-redux'
ReactDom.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
// 在组件中
import {connect} from 'react-redux'
// 参数一: mapStateToProps = (state)=>{return {num: state}}) // 返回一个对象
// 参数二:
// mapDispatchToProps = dispatch => {
// return {add: ()=>dispatch({type: 'add'})}
// }
@connect(
state=>({num: state}),
{
add:()=>({type: 'add'}), // actionCreators,
minus:()=>(type:'minus')
}
// 等价于下面这个完整的写法
dispatch => ({
add: ()=> dispatch({type:'add'}),
minus: ()=> dispatch({type:'minus'})
// dispatch里是一个action对象, action对象其实可以随便写,比如也可以加个
// {type:'add', color:'red'},只需action.color获取即可
})
)
class Example extends React.Component {
render(){
return(
<div><h1>{this.props.num}</h1>
<button onCLick={this.props.add}></button>
<button onCLick={this.props.minus}></button>
// 等价于下面,如果像下面这样写就不要actionCreators了
// <button onCLick={this.props.dispatch({type:'add'})}></button>
</div>
)
}
}
export default Example
异步
其实开发中最常见的问题是异步问题, redux是不支持异步的, 实现异步需要加中间件
Action => Middleware => Store(Reducer)
npm install redux-thunk redux-logger --save
// 在store中
import {createStore, applyMiddleWare} from 'redux'
import logger from 'redux-logger'
import thunk from 'redux-thunk'
const store = createStore(counterReducers, applyMiddleWare(logger, thunk))
// 在组件中
@connect(
state=>({num: state}),
{
...,
asyncAdd: ()=>dispatch=>{
// 在这里执行异步操作
setTimeout(()=>{
// 异步结束后,手动执行dispatch
dispatch({type:'add'})
},1000)
}
}
)
代码优化
一般action不会写到组件里, 写到store里, 在store里建立counter.js
// action
export const add = ()=>({type:'add'});
export const minus = ()=>({type:'minus'});
export const asyncAdd = ()=>dispatch=>{
// 这里执行异步代码
setTimeout(()=>{
// 异步执行完后dispatch
dispatch({type: 'add'})
})
}
// reducer也要单独分离出来, 也写在counter.js里
export const counterReducers = function(state=1,action) {
switch(action.type){
case 'add':
return state+1;
case 'minus':
return state-1;
default:
return state;
}
}
// store里的index.js
import {combineReducers} from 'redux'
const store = createStore(
combineReducers({counter: counterReducers}),
applyMiddleWare(logger, thunk)
)
// 组件中
@connect(
state => ({num: state.counter}),
{add, minus, asyncAdd}
)