组件
,这使得 Redux store 能够在应用的其他地方使用(即:store只需在入口文件传递一次,其他需要store的容器组件中都可以获取)。connect API
,去官网去官网:# If you use npm:
npm install react-redux
# Or if you use Yarn:
yarn add react-redux
去包裹我们的应用。:
import CountNumRedux from "../components/CountNumRedux";
import { connect } from "react-redux";
import store from '../redux/store'
//这里ownProps如果用不到的话,可以不传,可以只传state
const mapStateToProps = (state, ownProps) => ({
// ...依据 state 和 自定义 ownProps 生成 computed data
/**
* 即状态统一在容器组件中管理
* UI组件使用的话直接通过props取就行了,这种方式也相当于通过props传递
* 如果监听state的变化,一有变化就调用,并把state通过props传递给UI组件
*/
count:state
// name:'麦兜'
});
const mapDispatchToProps = ()=>({
// ... 通常是一个充满 action creators 的 object
addNumber:(number)=>{
store.dispatch(
{ type: 'INCREMENT', number:number }
)
},
reduceNumber:(number)=>{
store.dispatch(
{ type: 'DECREMENT', number:number }
)
}
});
// // 1. `connect` 返回一个接收要包装的组件的新函数:
// const connectToStore = connect(mapStateToProps, mapDispatchToProps);
// // 2. 并且该函数返回连接的,包装的组件:
// const ConnectedComponent = connectToStore(Component);
// 通常我们会将两者一步完成,像这样:
const CountNumContainer = connect(mapStateToProps, mapDispatchToProps)(CountNumRedux);
export default CountNumContainer;
import { createRef } from "react";
// import store from '../redux/store'
// import countAction from '../redux/countAction'
function CountNumRedux(props){
console.log(props);
// const [count,setCount] = useState(0);
const numberRef = createRef();
function add(){
let number = numberRef.current.value;
// console.log(typeof number); //string
// store.dispatch(countAction.incrementNum(parseInt(number)));
props.addNumber(parseInt(number));
}
function subtract(){
let number = parseInt(numberRef.current.value);
props.reduceNumber(number);
}
// useEffect(()=>{
// store.subscribe(()=>{
// console.log('订阅更新,打印2-----',store.getState());
// setCount(store.getState());
// });
// });
return(
<div>
{/* 当前数字是:{count}
当前数字是:{store.getState()} */}
当前数值是:{props.count}
<br />
浮动数字:<input type="number" ref={numberRef}/>
<br /><br />
<button onClick={add}>点我 加数</button> <br /><br />
<button onClick={subtract}>点我 减数</button>
</div>
)
}
export default CountNumRedux;
主要优化 mapDispatchToProps,用封装好的action,如下:
import CountNumRedux from "../components/CountNumRedux";
import { connect } from "react-redux";
// import store from '../redux/store'
import {incrementNum,decrementNum} from "../redux/countAction";
const mapStateToProps = (state) => ({
count:state
});
// const mapDispatchToProps = ()=>({
// addNumber:(number)=>{
// store.dispatch(
// { type: 'INCREMENT', number:number }
// )
// },
// reduceNumber:(number)=>{
// store.dispatch(
// { type: 'DECREMENT', number:number }
// )
// }
// });
/**
* 1. dispatch:react-redux 会将dispatch传入,所以不用引入store来调了
* 2. 引入已经封装好的action:countAction
*/
const mapDispatchToProps = (dispatch)=>({
addNumber:(number)=>{
dispatch( incrementNum(number) )
},
reduceNumber:(number)=>{
dispatch( decrementNum(number) )
}
});
const CountNumContainer = connect(mapStateToProps, mapDispatchToProps)(CountNumRedux);
export default CountNumContainer;
/**
* 优化2
*/
const mapDispatchToProps = {
//通常是一个充满 action creators 的 object
addNumber: incrementNum, //addNumber:是通过props传递给UI组件的方法, incrementNum:是封装好的action函数
reduceNumber: decrementNum
}
对比一下:import CountNumRedux from "../components/CountNumRedux";
import { connect } from "react-redux";
import {incrementNum,decrementNum} from "../redux/countAction";
const mapStateToProps = (state) => ({
count:state
});
const mapDispatchToProps = {
//通常是一个充满 action creators 的 object
addNumber: incrementNum, //addNumber:是通过props传递给UI组件的方法, incrementNum:是封装好的action函数
reduceNumber: decrementNum
}
const CountNumContainer = connect(mapStateToProps, mapDispatchToProps)(CountNumRedux);
export default CountNumContainer;
import { createRef } from "react";
function CountNumRedux(props){
console.log(props);
const numberRef = createRef();
function add(){
let number = numberRef.current.value;
props.addNumber(parseInt(number));
}
function subtract(){
let number = parseInt(numberRef.current.value);
props.reduceNumber(number);
}
return(
<div>
{/* 当前数字是:{count}
当前数字是:{store.getState()} */}
当前数值是:{props.count}
<br />
浮动数字:<input type="number" ref={numberRef}/>
<br /><br />
<button onClick={add}>点我 加数</button> <br /><br />
<button onClick={subtract}>点我 减数</button>
</div>
)
}
export default CountNumRedux;
import CountNumContainer from './container/CountNumContainer.jsx'
function App() {
return (
<div>
{/* */}
<CountNumContainer/>
</div>
);
}
export default App;
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(
<Provider store={store}>
<App />
</Provider>
);
export default root;
随着应用变得越来越复杂,可以考虑将 reducer 函数 拆分成多个单独的函数,拆分后的每个函数负责独立管理 state 的一部分。
combineReducers 辅助函数的作用是,把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore 方法。
合并后的 reducer 可以调用各个子 reducer,并把它们返回的结果合并成一个 state 对象。 由 combineReducers() 返回的 state 对象,会将传入的每个 reducer 返回的 state 按其传递给 combineReducers() 时对应的 key 进行命名。
示例:
rootReducer = combineReducers({potato: potatoReducer, tomato: tomatoReducer})
// 这将返回如下的 state 对象
{
potato: {
// ... potatoes, 和一些其他由 potatoReducer 管理的 state 对象 ...
},
tomato: {
// ... tomatoes, 和一些其他由 tomatoReducer 管理的 state 对象,比如说 sauce 属性 ...
}
}
关于 combineReducers()
函数 的介绍来源于官网,关于combineReducers()
更多讲解,可去官网,如下:
https://cn.redux.js.org/api/combinereducers.
combineReducers()
函数时也说了,这里就不多说了,也可以去官网看拆分reducer逻辑,地址如下:combineReducers()
的,你现在可以不用知道其他3个reducer长什么样,只要他们3个都暴露出了就行,所以我这里直接介绍合并,如下:catAction 如下:
function changeCatKindAction(newKind){
return {
type: 'CHANGE_CAT_KIND',
kind: newKind
}
}
export {changeCatKindAction}
dogAction 如下:
function addDogAction(dogObj){
return {
type:'ADD_DOG',
dog:dogObj
}
}
export {addDogAction}
前三个如下:
const catKindInit = '布偶';
function catReducer(state=catKindInit, action){
switch (action.type) {
case 'CHANGE_CAT_KIND':
return action.kind;
default:
return state;
}
}
export default catReducer;
const dogInit = [];
// const dogInit = [{dogName:'狗狗',dogAge:1}];
function dogReducer(state = dogInit, action){
const {type,dog} = action;
switch (type) {
case 'ADD_DOG':
return [...state,dog];
default:
return state;
}
}
export default dogReducer;
const adminInit = {parkAdmin:'素素',parkAdminPhone:'176XXXXX'};
function PetParkReducer(state=adminInit, action){
return state; //没有action,初始state不需要修改,直接返回
}
export default PetParkReducer;
最终的如下:
import catReducer from "./catsReducer";
import dogReducer from "./dogReducer";
import petsParkReducer from "./petsParkReducer";
import { combineReducers } from "redux";
/**
* 1. 合并后的 reducer 可以调用各个子 reducer,并把它们返回的结果合并成一个 state 对象。
* 2. 由 combineReducers() 返回的 state 对象,
* 会将传入的每个 reducer 返回的 state 按其传递给 combineReducers() 时对应的 key 进行命名。
*/
const rootReducer = combineReducers({
petParkState: petsParkReducer,
dogState: dogReducer,
catState: catReducer,
});
export default rootReducer;
CatContainer.jsx 如下:
import { connect } from "react-redux";
import { useRef } from "react";
import {changeCatKindAction} from '../redux/actions/CatAction'
//1. UI组件
function CatUI(props){
const catKindNode = useRef();
function chagePopularKind(){
const newKind = catKindNode.current.value;
props.changKind(newKind);
}
return(
<div>
<h1>我是cat组件</h1>
今日最受欢迎的小猫品种是:{props.popularCatKind} <br/><br/>
<input type="text" ref={catKindNode} placeholder="请输入今日最受欢迎的"/>
<button onClick={chagePopularKind}>修改最受欢迎的小猫品种</button>
<br />
今日管理员是:{props.guanliyuan} <br/>
管理员:{props.guanliyuan2.parkAdmin}
</div>
)
}
//2. 容器组件
function mapStateToProps(state) {
return {
popularCatKind: state.catState,
guanliyuan: state.petParkState.parkAdmin, //可以直接访问其中某个属性
guanliyuan2: state.petParkState, //也可以直接访问整个对象
}
}
const mapDispatchToProps = {
changKind: changeCatKindAction
}
const CatContainer = connect(mapStateToProps,mapDispatchToProps)(CatUI);
export default CatContainer;
DogContainer.jsx 如下:
import { useRef } from "react";
import { connect } from "react-redux"
import { addDogAction } from "../redux/actions/DogAction";
//1. 定义UI组件
function DogUI(props){
// console.log(props);
const dogList = props.dogListState;//获取狗狗列表信息
const dogNameRef = useRef();
const dogAgeRef = useRef();
function addParkDog(){
const dogName = dogNameRef.current.value;
const dogAge = dogAgeRef.current.value;
const dogObj = {dogName:dogName,dogAge:dogAge}
props.addOneDog(dogObj);
}
return(
<div>
<h1>我是dog组件</h1> <br />
1. 狗狗园区地址:{props.dogParkAdress} <br /><br />
2.
狗狗姓名:<input type="text" ref={dogNameRef} /> <br />
狗狗年龄:<input type="number" ref={dogAgeRef}/>
<button onClick={addParkDog}>添加狗狗</button> <br /><br />
3. 狗狗列表信息:
<ul>
{
dogList.map((dog,index)=>(
<li key={index}>{dog.dogName}---{dog.dogAge}</li>)
)
}
</ul>
</div>
)
}
//2.容器组件 并导出容器组件
const mapStateToProps = (state)=>{
/**
* 1. 返回的是一个对象(dog组件 管理自己组件的state)
* 2. 语法问题:当返回的是一个对象时,用一对()括起来,否则语法报错
*/
return(
{
dogListState:state.dogState,
dogParkAdress:'北京海淀区'
}
)
}
const mapDispatchToProps = {
addOneDog: addDogAction
}
const DogContainer = connect(mapStateToProps,mapDispatchToProps)(DogUI);
export default DogContainer;
PetParkContainer.jsx 如下:
import { connect } from "react-redux";
import { useState } from "react";
//1. UI组件
function PetParkUI(props){
console.log(props);
const [closeFlag,setCloseFlag] = useState(false);
console.log(closeFlag);
return(
<div>
<h1>我是PetPark组件</h1>
1. 管理员:{props.parkAdminInfo.parkAdmin} <br />
管理员电话:{props.parkAdminInfo.parkAdminPhone} <br />
2. 现有的宠物有:{JSON.stringify(props.petKinds)} <br />
3. 雨天是否闭园:{closeFlag ? '是' : '否'} <br /><br />
今日猫猫种类王是:{props.catKindKing} <br /><br />
今日dog园区有多少条狗狗:{props.dogsNum}
</div>
)
}
//2.容器组件
const mapStateToProps = (state)=>({
parkAdminInfo: state.petParkState,//这个交给react-redux管理的可以共享
petKinds: ['猫猫','狗狗'] ,//这个如果是自身组件用的,可以用useState放自身组件上
//下面是数据共享的
catKindKing: state.catState, //直接取cat组件里的状态
dogsNum: state.dogState.length
})
//connect 的两个参数都是可选的,可传可不传
const PetParkContainer = connect(mapStateToProps)(PetParkUI);
export default PetParkContainer;