大家好,我是梅巴哥er
。上篇介绍了redux , react-redux以及redux调试工具的使用,那么本篇就写一个redux案例,加深对redux的理解。另外,因为这个案例过于简单,只用到一个reducer,所以本篇就没有用到combineReducers
。
老规矩,写代码前,先来看下渲染效果图:
(还好这里点击按钮会有个动效,能看到我点击了哪个按钮😂)
左边是渲染结果,右边是redux调试工具的显示内容。
案例功能说明:
- 左边是select选择框,右边依次是 + - 和异步增加的按钮
- 选择select,点击按钮依次渲染不同的效果
- 点击的次数times在0和1次时,显示time,2次以上时,显示times复数形式。
- 当点击-号时,如果time会变成负数,那就不更新状态,同时提示“次数不能为负数”
项目结构:
代码:(注:以下代码均写在src文件夹里)
1,入口文件index.js
import React from 'react'
import ReactDOM from 'react-dom'
// import App1 from './components/App1'
import App3 from './containers/App3' // 这里改成了从容器组件中引入带有connect的app3
import store from './redux/store'
import {Provider} from 'react-redux'
//监听
// function render() {
// ReactDOM.render(
// <App2 store={store} />, // 把store传给子组件
// document.getElementById('root')
// )
// }
// render()
// store.subscribe(render)
// 有了Provider,就不需要做监听了
// 把要渲染的根组件放在Provider组件里,然后store交给Provider就可以了
ReactDOM.render(
<Provider store={store}>
<App3 /> {/* 根组件也改成了app3 */}
</Provider>,
// <App2 store={store} />, // 把store传给子组件
document.getElementById('root')
)
2,UI组件App2.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class App2 extends Component {
// 改用react-redux后,要先声明需要接收的属性
static propTypes = {
count: PropTypes.number.isRequired,
incrementAction: PropTypes.func.isRequired,
decrementAction: PropTypes.func.isRequired,
delayAddAction: PropTypes.func.isRequired
}
increment = () => {
// 1,获取选择框里的值
const number = this.select.value*1 // *1是为了把字符串转换成数字类型
// 调用store的方法更新状态
// this.props.store.dispatch(incrementAction(number))
// react-redux 这里改成:
this.props.incrementAction(number)
}
decrement = () => {
// 1,获取选择框里的值
const number = this.select.value*1 // *1是为了把字符串转换成数字类型
// 调用store的方法更新状态
// this.props.store.dispatch(decrementAction(number))
// react-redux 这里改成:
this.props.decrementAction(number)
}
delayAdd = () => { // 异步延时增加
const number = this.select.value*1
this.props.delayAddAction(number)
}
render() {
// const count = this.props.store.getState()
// react-redux 这里也要改一下:
const {count} = this.props
return (
<div>
<p>click { count } time{ count >= 2 ? <span>s</span> : null } </p>
<div>
<select ref={select => this.select = select} >
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={ this.increment } >+</button>
<button onClick={ this.decrement }>-</button>
<button onClick={this.delayAdd} >异步延时增加</button>
</div>
</div>
)
}
}
3,容器组件App3.js
// import React, { Component } from 'react'
import {incrementAction, decrementAction, delayAddAction} from '../redux/actions'
import {connect} from 'react-redux'
import App2 from '../components/App2'
// connect连接组件和redux
export default connect(
state => ({count: state}), // 这里的state是reducer管理的state
{incrementAction, decrementAction, delayAddAction} // 这俩是action函数。这里最好和声明接收的参数保持一致,就更简洁了。
)(App2) //App2是连接的组件
4,action类型(事件名称)action-types.js
// 所有的action的类型(type)都写在这里,方便识别和查找,以免重复
// 增加值
export const INCREMENT = 'increment'
// 减少值
export const DECREMENT = 'decrement'
5,action对象,因为action的数据是以参数的形式传递过来的,所以这里写成函数返回的action对象。
action.js
import { number } from "prop-types"
// 包含所有的action对象
import { INCREMENT, DECREMENT } from "./action-types"
// 增加的action
export const incrementAction = (number) => ({
type: INCREMENT,
data: number
})
// 减少的action
export const decrementAction = (number) => ({
type: DECREMENT,
data: number
})
// 异步延时增加
export const delayAddAction = (number) => {
return dispatch => {
setTimeout(() => {
// 所有的异步action 都有同步action去分事件
dispatch(incrementAction(number))
}, 1000);
}
}
6,仓库store.js
(仓库是唯一的,不用加s)
import {createStore, applyMiddleware} from 'redux'
import {counter} from './reducers'
import thunk from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'
// 生成一个store对象,连接管理员counter
const store = createStore(
counter,
composeWithDevTools(applyMiddleware(thunk))
)
// console.log(store)
export default store
7,仓库管理员reducers.js
// 包含多个reducer函数的文件
import {INCREMENT, DECREMENT} from './action-types'
export function counter(state = 0, action) {
switch(action.type) {
case INCREMENT:
return state + action.data
case DECREMENT:
if (state - action.data >= 0) {
return state - action.data
} else {
alert('次数不能为负!')
return state
}
// break
default:
return state
}
}