十三、Redux
一、Redux简介
1、store通过reducer创建了初始状态
2、组件通过store.getState()获取到store中保存的state挂载在了自己的状态上
3、用户产生了操作,调用了actions 的方法
4、actions的方法被调用,创建了带有标示性信息的action(描述对象)
5、actions将action通过调用store.dispatch方法发送到了reducer中
6、reducer接收到action并(根据标识信息判断之后)返回了新的state
7、store的state被reducer更改为新state的时候,store.subscribe方法里的回调函数会执行,此时就可以通知组件去重新获取state
二、三个核心概念
1、
1 {type: 'add', data: {name: '张三'}}
2、reducer
- 用于初始化状态,加工状态
- 纯函数,根据旧的state和action加工成新的state,两个参数
- 参数一:preState,之前的状态
- 参数二:action,动作对象
3、store
- 将state、action、reducer联系在一起的对象
- 如何得到此对象
import {createStore} from 'redux'
import reducer from './reducer'
const store = createStore(reducer)
- 此处的功能
getState()
:得到statedispatch(action)
三、三大原则
1、单一数据源
1 import React, { Component } from 'react' 2 3 export default class Count extends Component{ 4 // 组件私有状态 5 state = { count: 0 } 6 7 // 加 8 increment = () => { 9 const {value} = this.selectNumber 10 const {count} = this.state 11 this.setState({count: count + value*1}) 12 } 13 14 // 减 15 decrement = () => { 16 const {value} = this.selectNumbr 17 const {count} = this.state 18 this.setState({count: count - value*1}) 19 } 20 21 // 奇数加 22 incrementOdd = () => { 23 const {value} = this.selectNumber 24 const {count} = this.state 25 if(count % 2 !== 0){ 26 this.setState({count: count + value*1}) 27 } 28 } 29 30 // 异步加 31 incrementAsync = () => { 32 const {value} = this.selectNumber 33 const {count} = this.state 34 setTimeout(() => { 35 this.setState({count: count + value*1}) 36 },500) 37 } 38 39 render(){ 40 return ( 41 <div> 42 <h2>当前求和为:{this.state.count}</h2> 43 <select ref={c => this.selectNumber = c}> 44 <option value="1">1</option> 45 <option value="2">2</option> 46 <option value="3">3</option> 47 </select> 48 <button onClick={this.increment}>+</button> 49 <button onClick={this.dcrement}>-</button> 50 <button onClick={this.incrementOdd}>和为奇数+</button> 51 <button onClick={this.incrementAsync}>异步+</button> 52 </div> 53 ) 54 } 55 }
1 import React, { Component } from 'react' 2 import Count from './component/Count' 3 export default class App extends Component{ 4 render(){ 5 return( 6 <div> 7 <Count/> 8 </div> 9 ) 10 } 11 }
二、redux精简版
1、安装redux
npm i -S redux
1 /** 2 * 该文件专门用于暴露一个store对象,整个应用只有一个store对象 3 */ 4 5 // 引入createStore,用于创建redux中的store对象 6 import { createStore } from 'redux' 7 // 引入为Count组件服务的reducer 8 import countReducer from './count_reducer' 9 // 暴露store 10 export default createStore(countReducer)
1 // 创建仓库 2 const store = createStore( 3 reducer, 4 window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() 5 ); 6 7 // 如果需要使用安装的浏览器插件则需要添加 8 // window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
打开浏览器调试工具就可以看到类似如下界面:
3、新建
1 /** 2 * 1、为Count服务的reducer,本质是个函数 3 * 2、reducer函数接到两个参数,一个是之前的状态(preState),一个是动作对象(action) 4 */ 5 const initState = 0 6 7 export default function conuntReducer(preState = initState, action){ 8 // 从action对象中获取type,data 9 const { type, data } = action 10 // 根据type决定如何加工数据 11 switch (type) { 12 case 'incrment': 13 return preState + data 14 case 'decrement': 15 return preState - data 16 default: 17 return preState 18 } 19 }
1 import React, { Component } from 'react' 2 // 引入store,用于获取redux中保存的状态 3 import store from '../../redux/store' 4 export default class Count extends Component{ 5 // 组件私有状态 6 state = { carName: 'BMW' } 7 8 // 组件挂载时监测redux中数据的变化,手动调用render 9 componentDidMount(){ 10 store.subscribe(() => { 11 this.setState({}) 12 }) 13 } 14 // 也可用下面这种监测方式 15 // constructor(props){ 16 // super(props) 17 // // 获取初始数据 18 // this.state = store.getState() 19 // // 订阅数据(获取更新) 20 // store.subscribe(() => { 21 // this.setState(() => store.getState()) 22 // // this.setState((state) => (state = store.getState())) 23 // }); 24 // } 25 26 // 加 27 increment = () => { 28 const {value} = this.selectNumber 29 store.dispatch({type: 'increment', data: value*1}) 30 } 31 32 // 减 33 decrement = () => { 34 const {value} = this.selectNumbr 35 store.dispatch({type: 'decrement', data: value*1}) 36 } 37 38 // 奇数加 39 incrementOdd = () => { 40 const {value} = this.selectNumber 41 const count = store.getState() 42 if(count % 2 !== 0){ 43 store.dispatch({type: 'increment', data: value*1}) 44 } 45 } 46 47 // 异步加 48 incrementAsync = () => { 49 const {value} = this.selectNumber 50 setTimeout(() => { 51 store.dispatch({type: 'increment', data: value*1}) 52 },500) 53 } 54 55 render(){ 56 return ( 57 <div> 58 <h2>我是Count组件</h2> 59 <h4>当前求和为:{store.getState()}</h4> 60 <select ref={c => this.selectNumber = c}> 61 <option value="1">1</option> 62 <option value="2">2</option> 63 <option value="3">3</option> 64 </select> 65 <button onClick={this.increment}>+</button> 66 <button onClick={this.dcrement}>-</button> 67 <button onClick={this.incrementOdd}>和为奇数+</button> 68 <button onClick={this.incrementAsync}>异步+</button> 69 </div> 70 ) 71 } 72 }
1 import React, { Component } from 'react' 2 // 引入store,用于获取redux中保存的状态 3 import store from '../../redux/store' 4 5 export default class Count extends Component{ 6 // 组件私有状态 7 state = { carName: 'BMW' } 8 9 // 加 10 increment = () => { 11 const {value} = this.selectNumber 12 store.dispatch({type: 'increment', data: value*1}) 13 } 14 15 // 减 16 decrement = () => { 17 const {value} = this.selectNumbr 18 store.dispatch({type: 'decrement', data: value*1}) 19 } 20 21 // 奇数加 22 incrementOdd = () => { 23 const {value} = this.selectNumber 24 const count = store.getState() 25 if(count % 2 !== 0){ 26 store.dispatch({type: 'increment', data: value*1}) 27 } 28 } 29 30 // 异步加 31 incrementAsync = () => { 32 const {value} = this.selectNumber 33 setTimeout(() => { 34 store.dispatch({type: 'increment', data: value*1}) 35 },500) 36 } 37 38 render(){ 39 return ( 40 <div> 41 <h2>我是Count组件</h2> 42 <h4>当前求和为:{store.getState()}</h4> 43 <select ref={c => this.selectNumber = c}> 44 <option value="1">1</option> 45 <option value="2">2</option> 46 <option value="3">3</option> 47 </select> 48 <button onClick={this.increment}>+</button> 49 <button onClick={this.dcrement}>-</button> 50 <button onClick={this.incrementOdd}>和为奇数+</button> 51 <button onClick={this.incrementAsync}>异步+</button> 52 </div> 53 ) 54 } 55 }
1 import React from 'react' 2 import ReactDom from 'react-dom' 3 import App from './App' 4 import store from './redux/store' 5 6 ReactDom.render( 7 <App/>, 8 document.getElementById('root') 9 ) 10 11 // 监测redux中状态的改变,状态发生变化,重新渲染App组件 12 store.subscribe(() => { 13 ReactDom.render( 14 <App/>, 15 document.getElementById('root') 16 ) 17 })
三、redux完整版
1 /** 2 * 该文件专门为Count组件生成action对象 3 */ 4 export const createIncrementAction = (data) => { 5 return {type: 'increment', data} 6 } 7 // 上方简写 8 // export const createIncrementAction = data => ({type: 'increment', data}) 9 10 export const createDecrementAction = (data) => { 11 return {type: 'decrement', data} 12 }
1 import React, { Component } from 'react' 2 // 引入store,用于获取redux中保存的状态 3 import store from '../../redux/store' 4 // 引入actionCreater,用于创建action对象 5 import { 6 createIncrementAction, 7 createDecrementAction 8 } from '../../redux/count_action' 9 10 export default class Count extends Component{ 11 // 组件私有状态 12 state = { carName: 'BMW' } 13 14 // componentDidMount(){ 15 // // 监测redux中数据的变化,手动调用render 16 // store.subscribe(() => { 17 // this.setState({}) 18 // }) 19 // } 20 21 // 加 22 increment = () => { 23 const {value} = this.selectNumber 24 store.dispatch(createIncrementAction(value*1)) 25 } 26 27 // 减 28 decrement = () => { 29 const {value} = this.selectNumbr 30 store.dispatch(createDecrementAction(value*1)) 31 } 32 33 // 奇数加 34 incrementOdd = () => { 35 const {value} = this.selectNumber 36 const count = store.getState() 37 if(count % 2 !== 0){ 38 store.dispatch(createIncrementAction(value*1)) 39 } 40 } 41 42 // 异步加 43 incrementAsync = () => { 44 const {value} = this.selectNumber 45 setTimeout(() => { 46 store.dispatch(createIncrementAction(value*1)) 47 },500) 48 } 49 50 render(){ 51 return ( 52 <div> 53 <h2>我是Count组件</h2> 54 <h4>当前求和为:{store.getState()}</h4> 55 <select ref={c => this.selectNumber = c}> 56 <option value="1">1</option> 57 <option value="2">2</option> 58 <option value="3">3</option> 59 </select> 60 <button onClick={this.increment}>+</button> 61 <button onClick={this.dcrement}>-</button> 62 <button onClick={this.incrementOdd}>和为奇数+</button> 63 <button onClick={this.incrementAsync}>异步+</button> 64 </div> 65 ) 66 } 67 }
1 /** 2 * 定义action对象中type类型的常量值 3 */ 4 export const INCREMENT = 'increment' 5 export const DECREMENT = 'decrement'
1 /** 2 * 1、为Count服务的reducer,本质是个函数 3 * 2、reducer函数接到两个参数,一个是之前的状态(preState),一个是动作对象(action) 4 */ 5 import { INCREMENT, DECREMENT } from './constant' 6 7 const initState = 0 8 9 export default function conuntReducer(preState = initState, action){ 10 // 从action对象中获取type,data 11 const { type, data } = action 12 // 根据type决定如何加工数据 13 switch (type) { 14 case INCREMENT: 15 return preState + data 16 case DECREMENT: 17 return preState - data 18 default: 19 return preState 20 } 21 }
1 /** 2 * 该文件专门为Count组件生成action对象 3 */ 4 import { INCREMENT, DECREMENT } from './constant' 5 6 export const createIncrementAction = (data) => { 7 return {type: INCREMENT, data} 8 } 9 // 上方简写 10 // export const createIncrementAction = data => ({type: INCREMENT, data}) 11 12 export const createDecrementAction = (data) => { 13 return {type: DECREMENT, data} 14 }
四、redux异步action版
- 通常情况下,action只是一个对象,不能包含异步操作
-
-
同时对该部分代码测试的时候也比较困难,组件的业务逻辑也不清晰
-
使用中间件了之后,可以通过actionCreator异步编写action
-
这样代码就会拆分到actionCreator中,可维护性大大提高,可以方便于测试、复用
-
常见异步库:
-
Redux-thunk
-
Redux-saga
-
Redux-effects
-
Redux-side-effects
-
Redux-loop
-
Redux-observable
-
...
基于Promise的异步库:
-
Redux-promise
-
Redux-promises
-
Redux-simple-promise
-
Redux-promise-middleware
-
…
Redux官方出品的中间件库:redux-thunk
1 npm i -S redux-thunk
1 /** 2 * 该文件专门为用于暴露一个store对象,整个应用只有一个store对象 3 */ 4 5 // 引入createStore,用于创建redux中的store对象 6 import { createStore, applyMiddleware } from 'redux' 7 // 引入为Count组件服务的reducer 8 import countReducer from './count_reducer' 9 // 引入redux-thunk,用于支持异步action 10 import thunk from 'redux-thunk' 11 // 暴露store 12 export default createStore(countReducer, applyMiddleware(thunk))
1 /** 2 * 该文件专门为Count组件生成action对象 3 */ 4 import { INCREMENT, DECREMENT } from './constant' 5 6 // 同步action,就是指action的值为Object类型的一般对象 7 export const createIncrementAction = data => ({type: INCREMENT, data}) 8 export const createDecrementAction = data => ({type: DECREMENT, data}) 9 10 // 异步action,就是指action的值为函数,异步action中一般包含调用同步action 11 export const createIncrementAsyncAction = (data, time) => { 12 return (dispatch) => { 13 setTimeout(() => { 14 dispatch(createIncrementAction(data)) 15 }, time) 16 } 17 }
1 import React, { Component } from 'react' 2 // 引入store,用于获取redux中保存的状态 3 import store from '../../redux/store' 4 // 引入actionCreater,用于创建action对象 5 import { 6 createIncrementAction, 7 createDecrementAction, 8 createIncrementAsyncAction 9 } from '../../redux/count_action' 10 11 export default class Count extends Component{ 12 // 组件私有状态 13 state = { carName: 'BMW' } 14 15 // componentDidMount(){ 16 // // 监测redux中数据的变化,手动调用render 17 // store.subscribe(() => { 18 // this.setState({}) 19 // }) 20 // } 21 22 // 加 23 increment = () => { 24 const {value} = this.selectNumber 25 store.dispatch(createIncrementAction(value*1)) 26 } 27 28 // 减 29 decrement = () => { 30 const {value} = this.selectNumbr 31 store.dispatch(createDecrementAction(value*1)) 32 } 33 34 // 奇数加 35 incrementOdd = () => { 36 const {value} = this.selectNumber 37 const count = store.getState() 38 if(count % 2 !== 0){ 39 store.dispatch(createIncrementAction(value*1)) 40 } 41 } 42 43 // 异步加 44 incrementAsync = () => { 45 const {value} = this.selectNumber 46 // setTimeout(() => { 47 store.dispatch(createIncrementAsyncAction(value*1, 500)) 48 // },500) 49 } 50 51 render(){ 52 return ( 53 <div> 54 <h2>我是Count组件</h2> 55 <h4>当前求和为:{store.getState()}</h4> 56 <select ref={c => this.selectNumber = c}> 57 <option value="1">1</option> 58 <option value="2">2</option> 59 <option value="3">3</option> 60 </select> 61 <button onClick={this.increment}>+</button> 62 <button onClick={this.dcrement}>-</button> 63 <button onClick={this.incrementOdd}>和为奇数+</button> 64 <button onClick={this.incrementAsync}>异步+</button> 65 </div> 66 ) 67 } 68 }
五、react-redux基本使用
使用react-redux之后不需要在每个组件中手动的订阅数据的更新了,容器组件连接UI组件和redux
1、安装react-redux(react-redux依赖于redux,使用react-redux需要先安装redux)
npm i -S react-redux
1 import React, { Component } from 'react' 2 import Count from './component/Count' 3 import store from './redux/store' 4 5 export default class App extends Component{ 6 render(){ 7 return( 8 <div> 9 <Count store={store}/> 10 </div> 11 ) 12 } 13 }
1 /** 2 * Count组件的容器组件 3 */ 4 5 // 引入Count组件的UI组件 6 import CountUI from '../../components/Count' 7 import { 8 createIncrementAction, 9 createDecrementAction, 10 createIncrementAsyncAction 11 } from '../../redux/count_action' 12 13 // 引入connect用于连接UI组件与redux 14 import { connect } from 'react-redux' 15 16 /** 17 * 1、mapStateToProps函数返回一个对象 18 * 2、函数返回对象中的key作为传递给UI组件props中的key 19 * 3、mapStateToProps用于传递状态 20 */ 21 function mapStateToProps(state){ 22 return {count: state} 23 } 24 25 /** 26 * 1、mapDispatchToProps函数返回一个对象 27 * 2、函数返回对象中的key作为传递给UI组件props中的key 28 * 3、mapDispatchToProps用于传递操作状态的方法 29 */ 30 function mapDispatchToProps(dispatch){ 31 return { 32 jia: number => dispatch(createIncrementAction(number)), 33 jian: number => dispatch(createDecrementAction(number)), 34 jiaAsync: (number, time) => dispatch(createIncrementAsyncAction(number,time)), 35 } 36 } 37 38 // 使用connect()()创建并暴露一个Count的容器组件 39 export default connect(mapStateToProps,mapDispatchToProps)(CountUI)
1 import React, { Component } from 'react' 2 3 export default class Count extends Component{ 4 // 组件私有状态 5 state = { carName: 'BMW' } 6 7 // 加 8 increment = () => { 9 const {value} = this.selectNumber 10 this.props.jia(value*1) 11 } 12 13 // 减 14 decrement = () => { 15 const {value} = this.selectNumbr 16 this.props.jian(value*1) 17 } 18 19 // 奇数加 20 incrementOdd = () => { 21 const {value} = this.selectNumber 22 if(count % 2 !== 0){ 23 this.props.jia(value*1) 24 } 25 } 26 27 // 异步加 28 incrementAsync = () => { 29 const {value} = this.selectNumber 30 this.props.jiaAsync(value*1, 500) 31 } 32 33 render(){ 34 return ( 35 <div> 36 <h2>我是Count组件</h2> 37 <h4>当前求和为:{this.props.count}</h4> 38 <select ref={c => this.selectNumber = c}> 39 <option value="1">1</option> 40 <option value="2">2</option> 41 <option value="3">3</option> 42 </select> 43 <button onClick={this.increment}>+</button> 44 <button onClick={this.dcrement}>-</button> 45 <button onClick={this.incrementOdd}>和为奇数+</button> 46 <button onClick={this.incrementAsync}>异步+</button> 47 </div> 48 ) 49 } 50 }
需要注意:让redux调试工具正常使用,需要调整store代码,也可参考Redux开发者工具
1 /** 2 * 1、compose用于redux调试工具 3 */ 4 import { createStore, applyMiddleware, compose } from 'redux' 5 import thunk from 'redux-thunk' 6 7 // 引入reducer 8 import countReducer from './reducers/count_reducer' 9 10 // 使redux调试工具生效 11 const composeEnhancers = 12 (typeof window !== "undefined" && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose; 13 14 export default createStore( 15 countReducer, 16 composeEnhancers(applyMiddleware(thunk)) 17 )
六、react-redux使用优化
1、react-redux优化一
1 /** 2 * Count组件的容器组件 3 */ 4 5 // 引入Count组件的UI组件 6 import CountUI from '../../components/Count' 7 import { 8 createIncrementAction, 9 createDecrementAction, 10 createIncrementAsyncAction 11 } from '../../redux/count_action' 12 13 // 引入connect用于连接UI组件与redux 14 import { connect } from 'react-redux' 15 16 /** 17 * 1、mapStateToProps函数返回一个对象 18 * 2、函数返回对象中的key作为传递给UI组件props中的key 19 * 3、mapStateToProps用于传递状态 20 */ 21 const mapStateToProps = state => ({count: state}) 22 23 /** 24 * 1、mapDispatchToProps函数返回一个对象 25 * 2、函数返回对象中的key作为传递给UI组件props中的key 26 * 3、mapDispatchToProps用于传递操作状态的方法 27 */ 28 const mapDispatchToProps = dispatch => ({ 29 jia: number => dispatch(createIncrementAction(number)), 30 jian: number => dispatch(createDecrementAction(number)), 31 jiaAsync: (number, time) => dispatch(createIncrementAsyncAction(number,time)), 32 }) 33 34 // 使用connect()()创建并暴露一个Count的容器组件 35 export default connect(mapStateToProps,mapDispatchToProps)(CountUI)
2、redux-redux优化二
1 /** 2 * Count组件的容器组件 3 */ 4 5 // 引入Count组件的UI组件 6 import CountUI from '../../components/Count' 7 import { 8 createIncrementAction, 9 createDecrementAction, 10 createIncrementAsyncAction 11 } from '../../redux/count_action' 12 13 // 引入connect用于连接UI组件与redux 14 import { connect } from 'react-redux' 15 16 // 使用connect()()创建并暴露一个Count的容器组件 17 export default connect( 18 state => ({count: state}), 19 20 // mapDispatchToProps的一般写法 21 // dispatch => ({ 22 // jia: number => dispatch(createIncrementAction(number)), 23 // jian: number => dispatch(createDecrementAction(number)), 24 // jiaAsync: (number, time) => dispatch(createIncrementAsyncAction(number,time)), 25 // }) 26 27 // mapDispatchToProps的简写 28 { 29 jia: createIncrementAction, 30 jian: createDecrementAction, 31 jiaAsync: createIncrementAsyncAction 32 } 33 )(CountUI)
3、react-redux优化三
1 import React from 'react' 2 import ReactDom from 'react-dom' 3 import App from './App' 4 import store from './redux/store' 5 import { Provider } from 'react-redux' 6 7 ReactDom.render( 8 <Provider store={store}> 9 <App/> 10 </Provider>, 11 document.getElementById('root') 12 )
2、将Count的UI组件整合到Count的容器组件中
1 /** 2 * Count的UI组件+容器组件 3 */ 4 5 import React, { Component } from 'react' 6 import { 7 createIncrementAction, 8 createDecrementAction, 9 createIncrementAsyncAction 10 } from '../../redux/count_action' 11 // 引入connect用于连接UI组件与redux 12 import { connect } from 'react-redux' 13 14 class Count extends Component{ 15 // 组件私有状态 16 state = { carName: 'BMW' } 17 18 // 加 19 increment = () => { 20 const {value} = this.selectNumber 21 this.props.jia(value*1) 22 } 23 24 // 减 25 decrement = () => { 26 const {value} = this.selectNumbr 27 this.props.jian(value*1) 28 } 29 30 // 奇数加 31 incrementOdd = () => { 32 const {value} = this.selectNumber 33 if(count % 2 !== 0){ 34 this.props.jia(value*1) 35 } 36 } 37 38 // 异步加 39 incrementAsync = () => { 40 const {value} = this.selectNumber 41 this.props.jiaAsync(value*1, 500) 42 } 43 44 render(){ 45 return ( 46 <div> 47 <h2>我是Count组件</h2> 48 <h4>当前求和为:{this.props.count}</h4> 49 <select ref={c => this.selectNumber = c}> 50 <option value="1">1</option> 51 <option value="2">2</option> 52 <option value="3">3</option> 53 </select> 54 <button onClick={this.increment}>+</button> 55 <button onClick={this.dcrement}>-</button> 56 <button onClick={this.incrementOdd}>和为奇数+</button> 57 <button onClick={this.incrementAsync}>异步+</button> 58 </div> 59 ) 60 } 61 } 62 63 // 使用connect()()创建并暴露一个Count的容器组件 64 export default connect( 65 state => ({count: state}), 66 { 67 jia: createIncrementAction, 68 jian: createDecrementAction, 69 jiaAsync: createIncrementAsyncAction 70 } 71 )
七、实现两个组件的数据共享
1、action和reducer集中管理
1、将src/redux/count_action.js
的count_action.js
更名为count.js
并放入新建的src/redux/actions
目录下
2、将src/redux/count_action.js
的count_reducer.js
更名为count.js
并放入新建的src/redux/reducers
目录下
2、新建Person组件
1 import React, { Component } from 'react' 2 3 export default class Person extends Component{ 4 render(){ 5 return ( 6 Person... 7 ) 8 } 9 }
1 import React, { Component } from 'react' 2 import Count from './containers/Count' 3 import Person from './containers/Person' 4 5 export default class App extends Component{ 6 render(){ 7 return ( 8 <div> 9 <Count/> 10 <hr/> 11 <Person/> 12 </div> 13 ) 14 } 15 }
1 /** 2 * 定义action对象中type类型的常量值 3 */ 4 export const INCREMENT = 'increment' 5 export const DECREMENT = 'decrement' 6 export const ADD_PERSON = 'add_person'
1 import { ADD_PERSON } from '../constant' 2 3 const initState = [{id: '001', name: 'tom', age: 18}] 4 5 esport default function personReducer(preState = initState, action){ 6 const { type, data } = action 7 switch(type){ 8 case ADD_PERSON: 9 return [data, ...preState] 10 default: 11 return preState 12 } 13 }
1 import { ADD_PERSON } from '../constant' 2 3 export const createAddPersonAction = personObj => ({type: ADD_PERSON, data: personObj})
1 /** 2 * 该文件专门为用于暴露一个store对象,整个应用只有一个store对象 3 */ 4 5 // 引入createStore,用于创建redux中的store对象 6 import { createStore, applyMiddleware, combineReducers } from 'redux' 7 // 引入为Count组件服务的reducer 8 import countReducer from './reducers/count' 9 // 引入为Person组件服务的reducer 10 import personReducer from './reducers/person' 11 // 引入redux-thunk,用于支持异步action 12 import thunk from 'redux-thunk' 13 14 // 暴露store 15 export default createStore( 16 combineReducers({ 17 he: countReducer, 18 rens:personReducer 19 }), 20 applyMiddleware(thunk) 21 )
1 /** 2 * 该文件专门为用于暴露一个store对象,整个应用只有一个store对象 3 */ 4 5 // 引入createStore,用于创建redux中的store对象 6 import { createStore, applyMiddleware, combineReducers } from 'redux' 7 // 引入为Count组件服务的reducer 8 import countReducer from './reducers/count' 9 // 引入为Person组件服务的reducer 10 import personReducer from './reducers/person' 11 // 引入redux-thunk,用于支持异步action 12 import thunk from 'redux-thunk' 13 // 引入redux-devtools-extension 14 import { composeWithDevTools } from 'redux-devtools-extension' 15 16 // 暴露store 17 export default createStore( 18 combineReducers({ 19 he: countReducer, 20 rens:personReducer 21 }), 22 composeWithDevTools(applyMiddleware(thunk)) 23 )
3、实现数据共享
1 import React, { Component } from 'react' 2 // 安装并使用nanoid 3 import { nanoid } from 'nanoid' 4 import { createAddPersonAction } from '../../redux/actions/person' 5 import { connect } from 'react-redux' 6 7 class Person extends Component{ 8 addPerson = () => { 9 const name = this.nameNode.value 10 const age = this.ageNode.value 11 const personObj = {id: nonoid(), name, age} 12 this.props.jiayiren(personObj) 13 this.nameNode.value = '' 14 this.ageNode.value = '' 15 } 16 render(){ 17 return ( 18 <div> 19 <h2>我是Person组件,上方和为:{this.props.he}</h2> 20 <input ref={c => this.nameNode = c} type="text" placeholder="输入名字"/> 21 <input ref={c => this.ageNode = c} type="text" placeholder="输入年龄"/> 22 <button onClick={this.addPerson}>添加</button> 23 <ul> 24 { 25 this.props.person.map(p => { 26 return <li key={p.id}>{p.name}--{p.age}</li> 27 }) 28 } 29 </ul> 30 </div> 31 ) 32 } 33 } 34 export default connect( 35 state = ({ 36 person: state.rens, 37 he: state.he 38 }), 39 { jiayiren: createAddPersonAction } 40 )(Person)
1 /** 2 * Count的UI组件+容器组件 3 */ 4 5 import React, { Component } from 'react' 6 import { 7 createIncrementAction, 8 createDecrementAction, 9 createIncrementAsyncAction 10 } from '../../redux/actions/count' 11 // 引入connect用于连接UI组件与redux 12 import { connect } from 'react-redux' 13 14 class Count extends Component{ 15 // 组件私有状态 16 state = { carName: 'BMW' } 17 18 // 加 19 increment = () => { 20 const {value} = this.selectNumber 21 this.props.jia(value*1) 22 } 23 24 // 减 25 decrement = () => { 26 const {value} = this.selectNumbr 27 this.props.jian(value*1) 28 } 29 30 // 奇数加 31 incrementOdd = () => { 32 const {value} = this.selectNumber 33 if(count % 2 !== 0){ 34 this.props.jia(value*1) 35 } 36 } 37 38 // 异步加 39 incrementAsync = () => { 40 const {value} = this.selectNumber 41 this.props.jiaAsync(value*1, 500) 42 } 43 44 render(){ 45 return ( 46 <div> 47 <h2>我是Count组件</h2> 48 <h4>当前求和为:{this.props.count},下方人数是:{this.props.renshu}</h4> 49 <select ref={c => this.selectNumber = c}> 50 <option value="1">1</option> 51 <option value="2">2</option> 52 <option value="3">3</option> 53 </select> 54 <button onClick={this.increment}>+</button> 55 <button onClick={this.dcrement}>-</button> 56 <button onClick={this.incrementOdd}>和为奇数+</button> 57 <button onClick={this.incrementAsync}>异步+</button> 58 </div> 59 ) 60 } 61 } 62 63 // 使用connect()()创建并暴露一个Count的容器组件 64 export default connect( 65 state => ({ 66 count: state.he, 67 renshu: state.rens.length 68 }), 69 { 70 jia: createIncrementAction, 71 jian: createDecrementAction, 72 jiaAsync: createIncrementAsyncAction 73 } 74 )(Count)
使用redux调试工具,也可在src/redux/store.js
加入如下代码
1 /** 2 * 1、combineReducers用于合并多个reducer 3 * 2、compose用于redux调试工具 4 */ 5 import { 6 createStore, applyMiddleware, combineReducers, compose 7 } from 'redux' 8 import thunk from 'redux-thunk' 9 10 // 引入reducer 11 import countReducer from './reducers/count_reducer' 12 import personReducer from './reducers/person_reducer' 13 14 // 使redux调试工具生效 15 const composeEnhancers = 16 (typeof window !== "undefined" && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose; 17 18 export default createStore( 19 combineReducers({ 20 he: countReducer, 21 rens: personReducer 22 }), 23 composeEnhancers(applyMiddleware(thunk)) 24 )
五、redux最终版代码封装
1、项目效果和文件路径
项目效果
文件路径
2、项目入口文件和根组件
1 import React from 'react'; 2 import ReactDOM from 'react-dom'; 3 import App from './App'; 4 import store from './redux/store' 5 import { Provider } from 'react-redux' 6 7 ReactDOM.render( 8 <Provider store={store}> 9 <App /> 10 </Provider>, 11 document.getElementById('root') 12 );
App.js
1 import React, { Component } from 'react' 2 import Count from './containers/Count' 3 import Person from './containers/Person' 4 5 export default class App extends Component { 6 render() { 7 return ( 8 <div> 9 <Count /> 10 <hr /> 11 <Person/> 12 </div> 13 ) 14 } 15 }
3、redux根文件
1 /** 2 * 1、combineReducers用于合并多个reducer 3 * 2、compose用于redux调试工具 4 */ 5 import { 6 createStore, applyMiddleware, combineReducers, compose 7 } from 'redux' 8 import thunk from 'redux-thunk' 9 10 // 引入reducer 11 import count from './reducers/count_reducer' 12 import persons from './reducers/person_reducer' 13 14 // 使redux调试工具生效 15 const composeEnhancers = 16 (typeof window !== "undefined" && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose; 17 18 export default createStore( 19 combineReducers({ 20 count, 21 persons 22 }), 23 composeEnhancers(applyMiddleware(thunk)) 24 )
4、action对象的常量文件
1 /** 2 * 定义action对象中的type类型常量值 3 */ 4 export const INCREMENT = 'increment' 5 export const DECREMENT = 'decrement' 6 export const ADD_PERSON = 'add_person'
5、reducer文件
1 /** 2 * count的reducer文件 3 */ 4 import { INCREMENT, DECREMENT } from '../constant' 5 6 // 初始化状态 7 const initState = 0 8 9 // 加工状态 10 export default function countReducer(preState = initState, action) { 11 //获取action里的type,data 12 const { type, data } = action 13 // 根据type决定如何加工数据 14 switch (type) { 15 case INCREMENT: 16 return preState + data 17 case DECREMENT: 18 return preState - data 19 default: 20 return preState 21 } 22 }
1 import { ADD_PERSON } from '../constant' 2 3 const initState = [{ id: '01', name: '张三', age: 18 }] 4 5 export default function personReducer(preState = initState, action) { 6 const { type, data } = action 7 switch (type) { 8 case ADD_PERSON: 9 return [data, ...preState] 10 default: 11 return preState 12 } 13 }
6、action文件
1 /** 2 * action文件:为action生成action对象,定义修改规则 3 */ 4 import { INCREMENT, DECREMENT } from '../constant' 5 6 export const increment = (data) => { 7 return { type: INCREMENT, data } 8 } 9 10 export const decrement = (data) => { 11 return { type: DECREMENT, data } 12 } 13 14 // 异步action,就是指action的值为函数 15 // 异步action一般会调用同步action 16 export const incrementAsync = (data, time) => { 17 return (dispatch) => { 18 setTimeout(() => { 19 dispatch(increment(data)) 20 }, time); 21 } 22 }
1 import { ADD_PERSON } from '../constant' 2 3 // 导出任务清单 4 export const addPerson = (personObj) => ({ 5 type: ADD_PERSON, data: personObj 6 })
7、组件内使用
1 /** 2 * Count容器组件+UI组件 3 */ 4 import React, { Component } from 'react' 5 // 引入action 6 import { 7 increment, 8 decrement, 9 incrementAsync 10 } from '@/redux/actions/count_action' 11 12 // 引入connect,连接容器组件与UI组件 13 import { connect } from 'react-redux' 14 15 // 定义UI组件 16 class CountUI extends Component { 17 state = { 18 carName: '组件自己的state' 19 } 20 increment = () => { 21 const { value } = this.selectNumber 22 this.props.increment(value*1) 23 } 24 decrement = () => { 25 const { value } = this.selectNumber 26 this.props.decrement(value*1) 27 } 28 incrementOdd = () => { 29 const { value } = this.selectNumber 30 if (this.props.count % 2 !== 0) { 31 this.props.increment(value*1) 32 } 33 } 34 incrementAsync = () => { 35 const { value } = this.selectNumber 36 this.props.incrementAsync(value*1, 1000) 37 } 38 render() { 39 return ( 40 <div> 41 <h2>当前求和为:{this.props.count},下方总人数为{ this.props.personCount }</h2> 42 <select ref={c => this.selectNumber = c} name="" id=""> 43 <option value="1">1</option> 44 <option value="2">2</option> 45 <option value="3">3</option> 46 </select> 47 <button onClick={this.increment}>+</button> 48 <button onClick={this.decrement}>-</button> 49 <button onClick={this.incrementOdd}>和为奇数加</button> 50 <button onClick={this.incrementAsync}>异步加</button> 51 </div> 52 ) 53 } 54 } 55 56 export default connect( 57 // 传递状态 58 state => ({ 59 count: state.count, 60 personCount: state.persons.length 61 }), 62 // 传递操作状态的方法 63 { 64 increment, 65 decrement, 66 incrementAsync 67 } 68 )(CountUI)
1 import React, { Component } from 'react' 2 import { nanoid } from 'nanoid' 3 import { connect } from 'react-redux' 4 import { 5 addPerson 6 } from '@/redux/actions/person_action' 7 8 class Person extends Component { 9 10 addPerson = () => { 11 const name = this.nameNode.value 12 const age = this.ageNode.value*1 13 const personObj = { id: nanoid(), name, age } 14 this.props.addPerson(personObj) 15 } 16 17 render() { 18 return ( 19 <div> 20 <h2>上方组件和为{ this.props.count }</h2> 21 <input ref={c => this.nameNode = c} type="text" placeholder="输入名字"/> 22 <input ref={c => this.ageNode = c} type="text" placeholder="输入年龄" /> 23 <button onClick={this.addPerson}>填加</button> 24 <ul> 25 { 26 this.props.persons.map((p) => { 27 return <li key={ p.id }>{ p.name }---{ p.age }</li> 28 }) 29 } 30 </ul> 31 </div> 32 ) 33 } 34 } 35 36 export default connect( 37 state => ({ 38 persons: state.persons, 39 count: state.count 40 }), 41 { 42 addPerson 43 } 44 )(Person)