react-redux、Provider、整合容器组件与UI组件、多组件数据共享
-
react-redux:
facebook团队发现程序员都喜欢用redux在react项目中管理自己的状态,于是就开发了react-redux,目的是让程序员更方便的在react中使用redux。

ui组件一般放在components文件夹下,容器组件放在container文件夹下
ui组件的容器组件不用自己去写,用react-redux去写。
安装react-redux
npm install react-redux
react-redux的注意事项:
1、每个ui组件都有一个容器组件,是福字关系
2、容器组件用来和redux打交道
3.ui组件不能有任何redux的api
4、容器组件会传给ui组件:a、redux保存的状态,b、用于操作状态的方法
5、容器传给ui组件的状态和操作状态的方法均通过props
用react-redux实现一下Count组件
一、在containers文件下创建对应的Count容器组件
containers/Count/index.jsx
// 引入Count的ui组件 import CountUI from '../../components/Count' import { createIncrementAction, createDecrementAction, createIncrementAsyncAction } from '../../redux/count_action' // 引入connect,用于连接UI组件与redux import { connect } from 'react-redux' /** * 1.mapStateToProps函数返回的是一个对象: * 2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value * 3.mapStateToProps用于传递状态 */ function mapStateToProps(state) { // a函数的参数就是store中的状态 return {count: state} } // 函数返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value---操作状态的方法 /** * 1.mapDispatchToProps函数返回的是一个对象: * 2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value * 3.mapDispatchToProps用于传递操作状态的方法 */ function mapDispatchToProps(dispatch) { return { jia: number => dispatch(createIncrementAction(number)), jian: number => dispatch(createDecrementAction(number)), jiaAsync: number => dispatch(createIncrementAsyncAction(number, 500)), } } // 使用connect()()创建并暴露出去一个容器组件 export default connect(mapStateToProps, mapDispatchToProps)(CountUI);
二、Count的ui组件
components/Count/index.jsx
import React, { Component } from 'react'
export default class Count extends Component {
// 加法
increment = () => {
const { value } = this.selectNumber;
this.props.jia(value*1);
}
// 减法
decrement = () => {
const { value } = this.selectNumber;
this.props.jian(value*1);
}
// 如果是奇数再加
incrementIfOdd = () => {
const { value } = this.selectNumber;
if(this.props.count % 2 !== 0) {
this.props.jia(value*1);
}
}
// 异步加
incrementAsync = () => {
const { value } = this.selectNumber;
this.props.jiaAsync(value*1)
}
render() {
console.log(this.props);
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>
)
}
}
三、容器组件的store由其父级通过props传入
这里的Count容器组件是app.jsx
import React, { Component } from 'react'
import Count from './containers/Count'
import store from './redux/store'
export default class app extends Component {
render() {
return (
<div>
<Count store={store} />
</div>
)
}
}
容器组件内的优化:
1、代码层面优化:把function写成箭头函数,直接传递到第一个参数(mapStateToProps)
2、react-redux的api层面优化:mapDispatchToProps可以写成对象形式,只需要把actiom_creator传递过去,react-redux会自动调用dispatch分发
containers/Count/index.jsx:
// 引入Count的ui组件 import CountUI from '../../components/Count' import { createIncrementAction, createDecrementAction, createIncrementAsyncAction } from '../../redux/count_action' // 引入connect,用于连接UI组件与redux import { connect } from 'react-redux' // 使用connect()()创建并暴露出去一个容器组件 export default connect( state => ({count: state}), // mapDispatchToProps的一般写法 // dispatch => ({ // jia: number => dispatch(createIncrementAction(number)), // jian: number => dispatch(createDecrementAction(number)), // jiaAsync: number => dispatch(createIncrementAsyncAction(number, 500)), // }) // mapDispatchToProps的简写 // 简写形式可以写成对象形式 // 把对应的action_creator传递过期就可以了 { jia: createIncrementAction, jian: createDecrementAction, jiaAsync: createIncrementAsyncAction, } )(CountUI);
Provider:
之前,在index.jsx内,我们为了监听store的变化触发组件render,写了这样一段代码:
// 监听store中值的变化,更新App组件,就不用在每个组件中监听store掉用render了 store.subscribe(() => { root.render(<App />); })
在容器组件内,为了把store传给容器组件,我们通过props的形式把store传了过去,如果项目中有很多的容器组件,每个都有传store,有些不方便,
react-redux的Provider可以优雅的把这两个问题解决,Provider会自动分析项目中的容器组件,把需要store的容器组件传递给每一个容器组件。
在入口文件index.jsx内这样写:
import React from 'react' import ReactDOM from 'react-dom/client'; import App from './App.jsx' import store from './redux/store' import { Provider } from 'react-redux' const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <Provider store={store}> <App /> </Provider> ); // // 监听store中值的变化,更新App组件,就不用在每个组件中监听store掉用render了 // store.subscribe(() => { // root.render(<App />); // })
在使用Count容器组件的地方就可以省去传store的代码:
import React, { Component } from 'react'
import Count from './containers/Count'
// import store from './redux/store'
export default class app extends Component {
render() {
return (
<div>
{/* <Count store={store} /> */}
<Count />
</div>
)
}
}
整合容器组件与ui组件:
上面的例子,我们写了一个Count组件,分别在components中写了一个Count的UI组件,在Container中写了对应的容器组件。
如果项目中有20个组件需要和redux打交道,因为每个UI组件都需要一个容器组件,那么我们就需要再写对应的20个容器组件,总共40个组件,这样就很不方便了,
我们完全可以在一个文件内写两个组件,一个是ui组件,一个是容器组件,最后把容器组件暴露出来:
原来的Count的UI组件:
import React, { Component } from 'react'
export default class Count extends Component {
// 加法
increment = () => {
const { value } = this.selectNumber;
this.props.jia(value*1);
}
// 减法
decrement = () => {
const { value } = this.selectNumber;
this.props.jian(value*1);
}
// 如果是奇数再加
incrementIfOdd = () => {
const { value } = this.selectNumber;
if(this.props.count % 2 !== 0) {
this.props.jia(value*1);
}
}
// 异步加
incrementAsync = () => {
const { value } = this.selectNumber;
this.props.jiaAsync(value*1)
}
render() {
console.log(this.props);
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>
)
}
}
原来的Count的容器组件:
// 引入Count的ui组件 import CountUI from '../../components/Count' import { createIncrementAction, createDecrementAction, createIncrementAsyncAction } from '../../redux/count_action' // 引入connect,用于连接UI组件与redux import { connect } from 'react-redux' // 使用connect()()创建并暴露出去一个容器组件 export default connect( state => ({count: state}), // mapDispatchToProps的一般写法 // dispatch => ({ // jia: number => dispatch(createIncrementAction(number)), // jian: number => dispatch(createDecrementAction(number)), // jiaAsync: number => dispatch(createIncrementAsyncAction(number, 500)), // }) // mapDispatchToProps的简写 // 简写形式可以写成对象形式 // 把对应的action_creator传递过期就可以了 { jia: createIncrementAction, jian: createDecrementAction, jiaAsync: createIncrementAsyncAction, } )(CountUI);
我们把上面的容器组件和UI组件整合到容器组件内,最后暴露出容器组件:
Containers/Count/index.jsx:
import React, { Component } from 'react'
import { createIncrementAction, createDecrementAction, createIncrementAsyncAction } from '../../redux/count_action'
// 引入connect,用于连接UI组件与redux
import { connect } from 'react-redux'
// UI组件
class Count extends Component {
// 加法
increment = () => {
const { value } = this.selectNumber;
this.props.jia(value*1);
}
// 减法
decrement = () => {
const { value } = this.selectNumber;
this.props.jian(value*1);
}
// 如果是奇数再加
incrementIfOdd = () => {
const { value } = this.selectNumber;
if(this.props.count % 2 !== 0) {
this.props.jia(value*1);
}
}
// 异步加
incrementAsync = () => {
const { value } = this.selectNumber;
this.props.jiaAsync(value*1)
}
render() {
console.log(this.props);
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(
// mapStateToProps
state => ({count: state}),
// mapDispatchToProps的一般写法
// dispatch => ({
// jia: number => dispatch(createIncrementAction(number)),
// jian: number => dispatch(createDecrementAction(number)),
// jiaAsync: number => dispatch(createIncrementAsyncAction(number, 500)),
// })
// mapDispatchToProps的简写
{
jia: createIncrementAction,
jian: createDecrementAction,
jiaAsync: createIncrementAsyncAction,
}
)(Count);
从0开始写一个极简版的Count组件:
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { createIncrementAction } from '../../redux/count_action'
class Count extends Component {
add = () => {
this.props.jiafa(1)
}
render() {
return (
<div>
<h2>当前求和为: {this.props.he}</h2>
<button onClick={this.add}>点我加1</button>
</div>
)
}
}
export default connect(
// 映射状态
state => ({ he: state }),
// 映射操作状态的方法
{
jiafa: createIncrementAction
}
)(Count)
多组件数据共享:
前面的例子我们把count_actios.js和count_reducer.js直接放在了redux目录下,这样下去,多个组件就产生很多的xxx_action.js和xxx_reducer.js,索性,在redux目录下创建两个文件夹:
actions和reducers分别用来放置各个组件的action和reducer文件。
我们再创建一个Person组件,让count钻进可以用person的数据,让person组件可以用count的数据,实现数据共享。
把redux文件夹:

在这里需要注意,之前redux中只维护了count组件的一个值,但是现在又要维护person组件的值,这里就需要借助react-redux中的一个函数 combineReducers,把所有需要维护的reducer组合到一起,传给store。
store.js:
/** * 该文件专门用于暴露一个store对象 */ // 引入createStore,专门创建redux中最为核心的store对象 import { createStore, applyMiddleware, combineReducers } from 'redux' // 引入为Count组件服务的reducer import countReducer from './reducers/count' // 引入为Count组件服务的reducer import personReducer from './reducers/person' // 引入redux-thunk,用于支持异步action import thunk from 'redux-thunk' // 汇总所有的reducer变为一个总的reducer const allReducer = combineReducers({ he: countReducer, rens: personReducer }) // 暴露store export default createStore(allReducer, applyMiddleware(thunk));
person组件要增加人,那么reducer中就要多一个增加人的动作类型
redux/constants.js:
/** * 改文件是专门定义action中type类型的常量值 */ export const INCREMENT = 'increment'; export const DECREMENT = 'decrement'; export const ADD_PERSON = 'add_person';
redux/reducers/count.js
/** * 1.该文件是创建一个专门为Count组件服务的reducer,reducer的本质就是一个函数 * 2.reducer函数会接到两个参数,分别是:之前的状态(preState)、动作对象(action) */ import { INCREMENT, DECREMENT } from '../constants' // preState初始值是 undefined, 此时我们给他一个初始值 const initState = 0; export default function countReducer(preState = initState, action) { // 从action对象中获取 type、data const { type, data } = action; switch(type) { case INCREMENT: // 如果加 return preState + data; case DECREMENT: // 如果减 return preState - data; default: return preState; } }
redux/reducers/person.js
import { ADD_PERSON } from '../constants'
// 初始化人的列表
const initState = [{ id: '001', name: 'tom', age: 18 }];
export default function personReducer(preState = initState, action) {
const { type, data } = action;
switch(type) {
case ADD_PERSON: // 添加一个人
return [data, ...preState]
default:
return preState;
}
}
redux/actions/count.js
/** * 改文件专门为Count组件生产action对象 */ import { INCREMENT, DECREMENT } from '../constants' // 同步action,就是指action的值为Object类型的一般对象 export const createIncrementAction = data => ({ type: INCREMENT, data }); export const createDecrementAction = data => ({ type: DECREMENT, data }); // 异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的(组件中的异步函数可以异步调用同步action) export const createIncrementAsyncAction = (data, time) => { return (dispatch) => { setTimeout(() => { dispatch(createIncrementAction(data)); },time) } }
redux/actions/person.js
import { ADD_PERSON } from '../constants'
// 创建增加一个人的action对象
export const createAddPersonAction = personObj => ({ type: ADD_PERSON, data: personObj });
以上准备好了redux,下面写Person组件
containers/Person/index.jsx
import React, { Component } from 'react'
import { nanoid } from 'nanoid'
import { connect } from 'react-redux'
import { createAddPersonAction } from '../../redux/actions/person'
class Person extends Component {
addPerson = () => {
const name = this.nameNode.value;
const age = this.ageNode.value;
const personObj = { id: nanoid(), name, age };
this.props.jiayiren(personObj);
this.nameNode.value = '';
this.ageNode.value = '';
}
render() {
return (
<div>
<h2>我是Person组件,上方组件求和为: {this.props.he}</h2>
<input ref={c => this.nameNode = c } type="text" placeholder="输入名字" />
<input ref={c => this.ageNode = c } type="text" placeholder="输入年龄" />
<button onClick={this.addPerson}>添加</button>
<ul>
{
this.props.yiduiyren.map(p => {
return <li key={p.id}>名字{p.name}--年龄{p.age}</li>
})
}
</ul>
</div>
)
}
}
export default connect(
// mapStateToProps 映射状态
state => ({
yiduiyren: state.rens,
he: state.he
}),
// mapDisPatchToProps 映射操作状态的方法
{
jiayiren: createAddPersonAction
}
)(Person)
containers/Count/index.jsx
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { createIncrementAction, createDecrementAction, createIncrementAsyncAction } from '../../redux/actions/count'
class Count extends Component {
// 加法
increment = () => {
const { value } = this.selectNumber;
this.props.jia(value*1);
}
// 减法
decrement = () => {
const { value } = this.selectNumber;
this.props.jian(value*1);
}
// 如果是奇数再加
incrementIfOdd = () => {
const { value } = this.selectNumber;
if(this.props.count % 2 !== 0) {
this.props.jia(value*1);
}
}
// 异步加
incrementAsync = () => {
const { value } = this.selectNumber;
this.props.jiaAsync(value*1)
}
render() {
console.log(this.props);
return (
<div>
<h1>当前求和为: {this.props.count}, 下方组件总人数为:{this.props.renshu}</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.he,
renshu: state.rens.length
}),
// 映射操作状态的方法
{
jia: createIncrementAction,
jian: createDecrementAction,
jiaAsync: createIncrementAsyncAction,
}
)(Count)
app.jsx
import React, { Component } from 'react'
import Count from './containers/Count'
import Person from './containers/Person'
export default class app extends Component {
render() {
return (
<div>
<Count />
<hr />
<Person />
</div>
)
}
}
入口文件index.js
import React from 'react' import ReactDOM from 'react-dom/client'; import App from './App.jsx' import store from './redux/store' import { Provider } from 'react-redux' const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <Provider store={store}> <App /> </Provider> );
react-redux也有开发者工具,不过需要配合一个插件使用
yarn add redux-devtools-extension
在store中引入
/** * 该文件专门用于暴露一个store对象 */ // 引入createStore,专门创建redux中最为核心的store对象 import { createStore, applyMiddleware, combineReducers } from 'redux' // 引入为Count组件服务的reducer import countReducer from './reducers/count' // 引入为Count组件服务的reducer import personReducer from './reducers/person' // 引入redux-thunk,用于支持异步action import thunk from 'redux-thunk' // 引入redux-devtools-extension import { composeWithDevTools } from 'redux-devtools-extension' // 汇总所有的reducer变为一个总的reducer const allReducer = combineReducers({ he: countReducer, rens: personReducer }) // 暴露store // composeWithDevTools,也是在第二个参数 // export default createStore(allReducer, applyMiddleware(thunk)); export default createStore(allReducer, composeWithDevTools(applyMiddleware(thunk)));
-

浙公网安备 33010602011771号