不用react-redux
我们现在要实现一个这样的功能:
有两个组件分别为Home 、About,数据都来源于redux,两个组件中都有加减功能,都会影响redux里面的counter数据
先来看如果我们不用react-redux要实现一个这样的功能要怎么来写?
首先我们定义action,我们肯定要去定义一些actionCreators来创造action对象,而且type事件既要被reducers来捕获,而且也要在actionCreators来定义action也需要,因此定义四个type事件(因为有4个功能):
export const INCREMENT = 'INCREMENT'
export const DECREMENT = 'DECREMENT'
export const ADD_NUMBER = 'ADD_NUMBER'
export const SUB_NUMBER = 'SUB_NUMBER'
接下来定义actionCreators
import {
ADD_NUMBER,
SUB_NUMBER,
INCREMENT,
DECREMENT,
} from './constant'
export const addAction = num => ({
type: ADD_NUMBER,
num,
})
export const subAction = num => ({
type: SUB_NUMBER,
num
})
export const incAction = () => ({
type: INCREMENT
})
export const decAction = () => ({
type: DECREMENT
})
接下来我们肯定要根据不同的type事件,来定义不同的reducers来分别做对应的处理,分别用switch-case来做判断:
import {
ADD_NUMBER,
SUB_NUMBER,
INCREMENT,
DECREMENT,
} from '../actionCreators/constant'
const defaultState = {
counter: 0
}
function reducer(state = defaultState, action) {
switch(action.type) {
case ADD_NUMBER:
return {...state, counter: state.counter + action.num}
case SUB_NUMBER:
return {...state, counter: state.counter - action.num}
case INCREMENT:
return {...state, counter: state.counter + 1}
case DECREMENT:
return {...state, counter: state.counter - 1}
default:
return state;
}
}
export default reducer
最后我们来写store的代码,我们用redux的createStore来创建store对象,并把前面的reducers丢进去:
import reducer from "./reducer";
import {createStore} from 'redux'
const store = createStore(reducer)
export default store;
然后我们需要定义home和about组件的代码,逻辑就是在render函数中return回jsx代码,引入store对象用来dispatch action,并且监听redux的变化进而做对应的处理,(这里应该是可以做性能的优化的,因为只要redux有变化,那么肯定会触发store.subscribe()执行里面的callback,然后执行this.setState(),然后重新调用render函数,但是有时候redux改变了但是组件里需要的数据并没有发生改变,那就没必要重新render,connect函数其实做了这方面的优化,做了浅层比较)。
这个事情其实可以做一个测试:
我们about组件并没有依赖redux的数据,但是做了监听,发现即使没有依赖但是redux发生了变化仍然会重新渲染about组件。
之后返回unSubscribe函数,在componentWillUnmount()中去调用,卸载监听。
在页面发生点击事件的时候,调用store.dispatch()来创建action对象进而去修改redux中的state:
Home:
import React from 'react'
import store from '../store'
import {
addAction,
subAction,
incAction,
decAction,
} from '../actionCreators';
export default class Home extends React.Component {
state = {
counter: store.getState().counter
}
addNumber = () => {
store.dispatch(incAction())
}
decNumber = () => {
store.dispatch(decAction())
}
addNumber5 = () => {
store.dispatch(addAction(5))
}
decNumber5 = () => {
store.dispatch(subAction(5))
}
componentDidMount() {
this.unSubscribe = store.subscribe(() => {
console.log(store.getState().counter)
this.setState({
counter: store.getState().counter
})
})
}
componentWillUnmount() {
this.unSubscribe();
}
render() {
return (
<div>
<h2>总数: {this.state.counter}</h2>
<button onClick={this.addNumber}>+1</button>
<button onClick={this.decNumber}>-1</button>
<button onClick={this.addNumber5}>+5</button>
<button onClick={this.decNumber5}>-5</button>
</div>
);
}
}
About
import React from 'react'
import store from '../store'
import {
addAction,
subAction,
incAction,
decAction,
} from '../actionCreators';
export default class About extends React.Component {
state = {
counter: store.getState().counter
}
addNumber = () => {
store.dispatch(incAction())
}
decNumber = () => {
store.dispatch(decAction())
}
addNumber5 = () => {
store.dispatch(addAction(5))
}
decNumber5 = () => {
store.dispatch(subAction(5))
}
componentDidMount() {
this.unSubscribe = store.subscribe(() => {
console.log(store.getState().counter)
this.setState({
counter: store.getState().counter
})
})
}
componentWillUnmount() {
this.unSubscribe();
}
render() {
return (
<div>
<h2>总数: {this.state.counter}</h2>
<button onClick={this.addNumber}>+1</button>
<button onClick={this.decNumber}>-1</button>
<button onClick={this.addNumber5}>+5</button>
<button onClick={this.decNumber5}>-5</button>
</div>
);
}
}
使用react-redux
通过上面的代码我们会发现几个问题:
- home和about重复代码太多
- 并没有做性能优化,redux发生改变了就会重新调用callback进而重新执行render函数
- 页面组件和redux交互并没有做很好的分离,当页面发生点击事件以后需要向redux发出dispatch,然后重新setState,通过react-redux我们可以只关心页面组件里的东西。
用react-redux优化以后:
import React, {Component} from 'react'
import store from '../store'
import {
addAction,
decAction,
} from '../actionCreators'
import {connect} from 'react-redux'
function Home(props) {
return (
<div>
<h2>总数:{props.counter}</h2>
<button onClick={props.add5}>+5</button>
<button onClick={props.sub5}>-5</button>
</div>
)
}
const mapStateToProps = state => ({
counter: state.counter
})
const mapDispatchToProps = dispatch => ({
add5() {
dispatch(addAction(5))
},
sub5() {
dispatch(decAction(5))
}
})
export default connect(mapStateToProps, mapDispatchToProps)(Home)
并用Provider将App组件包裹一层