react-redux和redux是两个不同的概念。
redux是一个重要的数据管理库。redux的作用是帮助处理应用程序中复杂的数据管理和状态管理,它可以让你的应用程序更加可维护和可扩展。
react-redux是一个react库,它可以帮助react开发者在react应用程序中集成redux。它通过提供一组特定于react的API来简化了redux的使用。
使用react-redux,你可以在react组件中直接操作redux中的状态和数据,而无需使用繁琐的API和命令。这样可以使代码更加简洁和易于维护。 总之,react-redux是一个非常有用的库,它可以使react开发者更加轻松地处理复杂的数据管理和状态管理。
根据模型图,改造求和案例
- 步骤1:清除Count组件里面的redux的所有API
import React, { Component } from 'react' export default class Count extends Component { increment = () => { // 普通加 // 1、获取用户选择的值 const { value } = this.selectNumber } decrement = () => { // 普通减 // 1、获取用户选择的值 const { value } = this.selectNumber } incrementIfOdd = () => { // 当前求和为奇数为 // 1、获取用户选择的值 const { value } = this.selectNumber } incrementAsync = () => { // 异步加 // 1、获取用户选择的值 const { value } = this.selectNumber } render() { return ( <div> <h1>当前求和为:????</h1> <select ref={(c) => (this.selectNumber = c)}> <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.incrementIfOdd}>当前求和为奇数为</button> <button onClick={this.incrementAsync}>异步加</button> </div> ) } }
- 步骤2:创建Count-UI组件的容器组件
文件:src/containers/Count/index.jsx
// 引入Count的UI组件 import CountUI from '../../components/Count' // 引入react-redux中的connect用于连接UI组件和容器组件 import { connect } from 'react-redux' // 创建并暴露一个容器组件 export default connect()(CountUI)
这里我们需要使用react-redux
来连接我们的UI
组件,所以我们需要安装依赖:
npm i react-redux
现在我们在App.jsx
里面将Count
的UI
组件替换为容器组件
import React, { Component } from 'react' // import Count from './components/Count' // 引入Count的容器组件 import Count from './containers/Count' // 引入store,用于传入容器组件 import store from './redux/store' export default class App extends Component { render() { return ( <div> <Count store={store}/> </div> ) } }
这里需要将store
通过props
的方式引入,否则会报错:
Could not find "store" in the context of "Connect(Count)". Either wrap the root component in a <Provider>, or pass a custom React context provider to <Provider> and the corresponding React context consumer to Connect(Count) in connect options.
现在运行我们可以正常看见修改后的页面:
- 步骤3:在容器组件里面传递UI组件状态和操作状态的方法
在这里我们就直接说了,在connect方法第一次执行的时候需要传递两个方法:mapStateToProps和mapDispatchToProps.
mapStateToProps
方法是用于传递UI组件状态mapDispatchToProps
方法是用于传递UI组件操作状态的方法
文件:src/containers/Count/index.jsx
// 引入Count的UI组件 import CountUI from '../../components/Count' // 引入action import { createIncrementAction, createDecrementAction, createIncrementAsyncAction } from '../../redux/count_action' // 引入react-redux中的connect用于连接UI组件和容器组件 import { connect } from 'react-redux' /** * 1.mapStateToProps函数返回的是一个对象 * 2.返回对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value * 3.mapStateToProps用于传递状态 * @param {*} state * @returns */ function mapStateToProps (state) { return {count: state} } /** * 1.mapDispatchToProps函数返回的是一个对象 * 2.返回对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value * 3.mapDispatchToProps用于传递操作状态的方法 * @param {*} dispatch * @returns */ function mapDispatchToProps (dispatch) { return { jia: num => dispatch(createIncrementAction(num)), jian: num => dispatch(createDecrementAction(num)), jiaAsync: (num,time) => dispatch(createIncrementAsyncAction(num,time)) } } // 创建并暴露一个容器组件 export default connect(mapStateToProps,mapDispatchToProps)(CountUI)
- 步骤4:在UI组件里面使用容器组件传递过来的状态与操作状态的方法
文件:src/components/Count/index.jsx
import React, { Component } from 'react' export default class Count extends Component { increment = () => { // 普通加 // 1、获取用户选择的值 const { value } = this.selectNumber this.props.jia(value*1) } decrement = () => { // 普通减 // 1、获取用户选择的值 const { value } = this.selectNumber this.props.jian(value*1) } incrementIfOdd = () => { // 当前求和为奇数为 // 1、获取用户选择的值 const { value } = this.selectNumber if (this.props.count %2 !== 0) { this.props.jia(value*1) } } incrementAsync = () => { // 异步加 // 1、获取用户选择的值 const { value } = this.selectNumber this.props.jiaAsync(value*1,500) } render() { return ( <div> <h1>当前求和为:{this.props.count}</h1> <select ref={(c) => (this.selectNumber = c)}> <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.incrementIfOdd}>当前求和为奇数为</button> <button onClick={this.incrementAsync}>异步加</button> </div> ) } }
- 最后查看效果
- 小节总结
(1). 明确两个概念:
1). UI组件:不能使用任何redux的api,只负责页面的呈现、交互等。
2). 容器组件:负责和redux通信,将结果交给UI组件。
(2). 如何创建一个容器组件——靠react-redux的connect函数
connect(mapStateToProps,mapDispatchToProps)(UI组件)
-mapStateToProps: 映射状态,返回值是一个对象
-mapDispatchToProps: 映射操作状态的方法,返回值是一个对象
(3). 备注1:容器组件中的store是靠props传进去的,而不是在容器组件中直接引入。
(4). 备注2:mapDispatchToProps可以是一个函数,也可以是一个对象。
优化代码
1、优化容器组件
将容器组件的connect
函数先使用箭头函数进行优化:
(1). 将普通函数改为箭头函数, src/containers/Count/index.jsx
// 引入Count的UI组件 import CountUI from '../../components/Count' // 引入action import { createIncrementAction, createDecrementAction, createIncrementAsyncAction } from '../../redux/count_action' // 引入react-redux中的connect用于连接UI组件和容器组件 import { connect } from 'react-redux' // function mapStateToProps (state) { // return {count: state} // } const mapStateToProps = state => ({count:state}) // function mapDispatchToProps (dispatch) { // return { // jia: num => dispatch(createIncrementAction(num)), // jian: num => dispatch(createDecrementAction(num)), // jiaAsync: (num,time) => dispatch(createIncrementAsyncAction(num,time)) // } // } const mapDispatchToProps = dispatch => ({ jia: num => dispatch(createIncrementAction(num)), jian: num => dispatch(createDecrementAction(num)), jiaAsync: (num,time) => dispatch(createIncrementAsyncAction(num,time)) }) // 创建并暴露一个容器组件 export default connect(mapStateToProps,mapDispatchToProps)(CountUI)
(2). 直接将箭头函数体作为connect
函数的参数
// 引入Count的UI组件 import CountUI from '../../components/Count' // 引入action import { createIncrementAction, createDecrementAction, createIncrementAsyncAction } from '../../redux/count_action' // 引入react-redux中的connect用于连接UI组件和容器组件 import { connect } from 'react-redux' // 创建并暴露一个容器组件 export default connect( state => ({count: state}), dispatch => ({ jia: num => dispatch(createIncrementAction(num)), jian: num => dispatch(createDecrementAction(num)), jiaAsync: (num,time) => dispatch(createIncrementAsyncAction(num,time)) }) )(CountUI)
(3). 将mapDispacthToProps
直接写为一个对象
// 引入Count的UI组件 import CountUI from '../../components/Count' // 引入action import { createIncrementAction, createDecrementAction, createIncrementAsyncAction } from '../../redux/count_action' // 引入react-redux中的connect用于连接UI组件和容器组件 import { connect } from 'react-redux' // 创建并暴露一个容器组件 export default connect( state => ({count: state}), { jia:createIncrementAction, jian:createDecrementAction, jiaAsync:createIncrementAsyncAction } )(CountUI)
这里可以有一点困惑,其实该对象被传递给UI组件使用时,简要的流程是这样的:
UI组件 ===> this.props.jia(value*1) // 但是jia对应的值其实是createIncrementAction这个函数 实际调用 ===> createIncrementAction(value*1) // 这时返回给store的值是一个action对象,而不是函数 最后store监测到值是一个action对象,直接调用reducer修改更新状态的值。
2、关闭redux的监听
原来我们修改redux
的状态,需要使用一个API
:store.subscribe
来监听状态的改变,再次渲染页面,现在使用了react-redux
,它已经帮我们做了状态变化的监听,不需要我们自己写了:
关闭在入口文件的监听事件:
import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; // import store from './redux/store'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <App /> </React.StrictMode> ); // store.subscribe(() => { // root.render( // <React.StrictMode> // <App /> // </React.StrictMode> // ); // })
3、使用Provider组件优化store对容器组件的传递
原来我们传递给容器组件的方式是在App组件里面通过props的方式将store传递给容器组件的,但是这样有一个问题:
import React, { Component } from 'react' // 引入Count的容器组件 import Count from './containers/Count' // 引入store,用于传入容器组件 import store from './redux/store' export default class App extends Component { render() { return ( <div> {/* 给容器组件传递store */} <Count store={store} /> <Count1 store={store} /> <Count2 store={store} /> <Count3 store={store} /> <Count4 store={store} /> </div> ) } }
假设如上代码,我们有很多个容器组件,难道我们需要一个一个的这样手写props
吗?这样很不合理,所以react-redux
提供了一个Provider
组件帮我们实现了对整个应用的容器组件传递store
,无需我们一个一个的props
手写传递。
(1). 将原来在APP组件传递store的方式去掉
import React, { Component } from 'react' // 引入Count的容器组件 import Count from './containers/Count' export default class App extends Component { render() { return ( <div> <Count/> </div> ) } }
(2). 在入口文件引入并使用Provider
组件
import React from 'react' import ReactDOM from 'react-dom/client' import App from './App' import store from './redux/store' import { Provider } from 'react-redux' const root = ReactDOM.createRoot(document.getElementById('root')) root.render( <React.StrictMode> <Provider store={store}> <App /> </Provider> </React.StrictMode> )
至此,无论有多少个容器组件,都无需我们手写props
进行传递store
了,非常人性化。
4、将容器组件与UI组件整合为一个文件
为了避免文件过多,将容器组件和UI组件合并为一个组件可以使代码更加简洁和易于维护。这样可以将UI组件的状态和属性与容器组件的状态和属性分离,使得组件之间的通信更加明确和紧密。此外,将容器组件和UI组件合并为一个组件也可以减少组件的数量,使得代码结构更加清晰和易于理解。
合并非常简单,就是将UI组件的代码搬到容器组件里面来即可。
// 引入action import { createIncrementAction, createDecrementAction, createIncrementAsyncAction } from '../../redux/count_action' // 引入react-redux中的connect用于连接UI组件和容器组件 import { connect } from 'react-redux' import React, { Component } from 'react' class Count extends Component { increment = () => { // 普通加 // 1、获取用户选择的值 const { value } = this.selectNumber this.props.jia(value*1) } decrement = () => { // 普通减 // 1、获取用户选择的值 const { value } = this.selectNumber this.props.jian(value*1) } incrementIfOdd = () => { // 当前求和为奇数为 // 1、获取用户选择的值 const { value } = this.selectNumber if (this.props.count %2 !== 0) { this.props.jia(value*1) } } incrementAsync = () => { // 异步加 // 1、获取用户选择的值 const { value } = this.selectNumber this.props.jiaAsync(value*1,500) } render() { return ( <div> <h1>当前求和为:{this.props.count}</h1> <select ref={(c) => (this.selectNumber = c)}> <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.incrementIfOdd}>当前求和为奇数为</button> <button onClick={this.incrementAsync}>异步加</button> </div> ) } } // 创建并暴露一个容器组件 export default connect( state => ({count: state}), /*dispatch => ({ jia: num => dispatch(createIncrementAction(num)), jian: num => dispatch(createDecrementAction(num)), jiaAsync: (num,time) => dispatch(createIncrementAsyncAction(num,time)) })*/ { jia:createIncrementAction, jian:createDecrementAction, jiaAsync:createIncrementAsyncAction } )(Count)
使用的时候,引入容器组件即可。
小节总结
- 优化
connect
函数的代码逻辑 - 引入
Provider
组件优化容器组件的store
传递 - 引入
react-redux
后无需自己监测redux
的变化 mapDispatchToProps
也可以简化写成对象形式- 一个组件要和
redux
“打交道”要经过几个步骤:
(1).定义好UI组件——不暴露 (2).引入connect函数生成一个容器组件,并暴露,写法如下: connect( state => ({key:value}), // 映射状态 {key:value} // 映射操作状态的方法 )(UI组件) (3).在UI组件中通过this.props.xxxx读取和操作状态