react-redux
和redux
是两个不同的概念。
redux
是一个重要的数据管理库。redux
的作用是帮助处理应用程序中复杂的数据管理和状态管理,它可以让你的应用程序更加可维护和可扩展。
react-redux
是一个react
库,它可以帮助react
开发者在react
应用程序中集成redux
。它通过提供一组特定于react
的API
来简化了redux
的使用。
使用react-redux
,你可以在react
组件中直接操作redux
中的状态和数据,而无需使用繁琐的API
和命令。这样可以使代码更加简洁和易于维护。 总之,react-redux
是一个非常有用的库,它可以使react
开发者更加轻松地处理复杂的数据管理和状态管理。
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>
)
}
}
文件: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 , or pass a custom React context provider to and the corresponding React context consumer to Connect(Count) in connect options.
现在运行我们可以正常看见修改后的页面:
在这里我们就直接说了,在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)
文件: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
可以是一个函数,也可以是一个对象。
将容器组件的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修改更新状态的值。
原来我们修改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(
//
//
//
// );
// })
原来我们传递给容器组件的方式是在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
了,非常人性化。
为了避免文件过多,将容器组件和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读取和操作状态