函数式编程中有一个非常重要的概念叫纯函数,JavaScript符合函数式编程的范式,所以也有纯函数的概念;
纯函数的维基百科定义:
当然上面的定义会过于的晦涩,简单总结一下:
那么上面又有一个概念,叫做副作用
纯函数在执行的过程中就是不能产生这样的副作用:副作用往往是产生bug的 “温床”。
来看一个对数组操作的两个函数:
slice 就是一个纯函数,不会修改数组本身,而 splice 函数不是一个纯函数;
为什么纯函数在函数式编程中非常重要
React 中就要求我们无论是函数还是 class 声明一个组件,这个组件都必须像纯函数一样,保护它们的props不被修改:
在接下来学习 redux 中,reducer 也被要求是一个纯函数。
JavaScript 开发的应用程序,已经变得越来越复杂了:
管理不断变化的 state 是非常困难的:
React 是在视图层帮助我们解决了DOM的渲染过程,但是State依然是留给我们自己来管理:
Redux 就是一个帮助我们管理State的容器:Redux 是 JavaScript 的状态容器,提供了可预测的状态管理;
Redux 除了和 React 一起使用之外,它也可以和其他界面库一起来使用(比如Vue),并且它非常小(包括依赖在内,只有2kb)
Redux 的核心理念非常简单。
整个应用程序错综复杂,当出现 bug 时,很难跟踪到底哪里发生的变化
const initialState ={
friends:[
{name:'foo',age:18},
{name:'coo',age:19},
{name:'hoo',age:20}
]
}
Redux 要求我们通过 action 来更新数据:
比如下面就是几个更新 friends 的 action:
const action1 = { type: 'ADD_FRIEND', info: {name:'licy',age:21} }
const action2 = { type: 'INC_AGE', index:0 }
const action3 = { type: 'CHANGE_NAME', playload: {index:0,newName:'lulu'} }
reducer 就是将 state 和 action 联系在一起的
function reducer(state = initialState, action) {
switch (action.type) {
case "ADD_FRIEND": // 添加一个朋友
return { ...state, friends: [...state.friend, action.info] }
case 'INC_AGE': // 给某个朋友的 age + 1
return {
...state, friends: state.friends.map((item, index) => {
if (index == action.index) {
return {...item,age:item.age+1}
}
return item;
})
}
case 'CHANGE_NAME':
return {
...state, friends: state.friends.map((item, index) => {
if (index === action.index) {
return {...item,name:action.newName}
}
return item;
})
}
default:
return state;
}
}
单一数据源
State是只读的
使用纯函数来执行修改
store.getState
来获取当前的 state;dispatch
来派发 action;如果我们将所有的逻辑代码写到一起,那么当 redux 变得复杂时,代码就难以维护。
store/index.js
文件:创建 storestore/reducer.js
文件:定义 reducerstore/actionCreators.js
文件:将所有 action 函数统一定义于此文件store/constants.js
文件:管理 派发 action 时 标注(type)的名称【示例:在 App.jsx 文件中点击修改 name 和 age 按钮,能修改 store 中的数据】
1、store/index.js
文件
import { createStore } from "redux"
import {reducer} from './reducer'
// 创建 store
export const store = createStore(reducer);
// 订阅 subscribe; 在订阅了的地方,就能在 state 更新的之后 触发回调
const unsubscribe = store.subscribe(() => {
console.log("订阅数据发生了变化", store.getState());
});
// 调用 unsubscribe 即可取消订阅
// unsubscribe()
2、store/reducer.js
文件
import { CHANGE_AGE, CHANGE_NAME } from "./constants";
// 初始化的数据
const initialState = {
name: "zs",
age: 20,
};
// 定义 reducer 函数: 纯函数
// 接收 2 个 参数
// 参数一: store中 目前保存的 state
// 参数二:本次需要更新的action(dispatch传入的action)
// 返回值: 返回值会作为 store之后存储的 state
export function reducer(state = initialState, action) {
console.log("reducer:", state, action);
// 如果有新数据更新,那么返回一个 新的 state
switch (action.type) {
case CHANGE_NAME:
return { ...state, name: action.name };
case CHANGE_AGE:
return { ...state, age: action.age };
default:
return state;
}
// 没有新数据更新, 那么返回之前的 state
return state;
}
3、store/actionCreators.js
文件
import {CHANGE_AGE,CHANGE_NAME} from './constants'
export const changeNameAction = (name) => ({
type: CHANGE_NAME,
name,
});
export const changeAgeAction = (age) => ({
type: CHANGE_AGE,
age,
});
4、store/constants.js
文件
// 定义常量的文件
export const CHANGE_NAME = 'change_name'
export const CHANGE_AGE = 'change_age'
5、App.jsx
文件
import React, { PureComponent } from 'react'
import { store } from "./store";
import {changeAgeAction,changeNameAction} from "./store/actionCreator"
export default class App extends PureComponent {
constructor() {
super()
this.state ={name:store.getState().name}
}
updateName() {
// 修改Store中的数据:必须用 action
// store.dispatch({ type: 'change_name', name: 'foo' }); // 和下面一行的效果一样
store.dispatch(changeNameAction('foo'));
}
updateAge() {
const data = store.getState();
// store.dispatch({type:'change_age',age:data.age + 1})
store.dispatch(changeAgeAction(data.age + 1));
}
componentDidMount() {
// 在组件挂载的时候去订阅 store;store的数据一更新,就会通知所有订阅者更新
this.unsubscribe = store.subscribe(() => {
const state = store.getState()
this.setState({
name:state.name // 把需要的数据更新到组件自身上
})
})
};
componentWillUnmount() {
this.unsubscribe();// 记得在组件卸载时,取消订阅
}
render() {
return (
<div>
<button onClick={(e) => this.updateName()}>修改name</button>
<button onClick={(e) => this.updateAge()}>修改age</button>
</div>
);
}
}
但是如上所示,仅仅在一个组件中,使用 state 的 数据 ,就已经很繁琐了,更别说 组件可能需要很多 state 中的数据,甚至其他组件也要使用;下文中会用 react-redux库 简化流程
认识一下 redux 在实际开发中的流程:
redux 和 react 没有直接的关系,你完全可以在 React, Angular, Ember, jQuery, or vanilla JavaScript 中使用 Redux。
尽管如此,redux 依然是和 React 库结合的更好,因为他们是通过state函数来描述界面的状态,Redux 可以发送状态的更新,让他们作出相应的更新。
redux 官方提供了 react-redux 的库,提供了 connect、Provider 等 API, 帮助我们连接 react 和 redux,实现的逻辑会更加的严谨和高效。
使用步骤:
一:下载 react-redux
库
npm i react-redux
二:过 react-redux 的 Provider
为后代组件注入 store
// 在 index.js 中,通过react-redux的Provider
// 使 Redux store 可用于任何需要访问 Redux store 的嵌套组件
import ReactDom from "react-dom/client";
import App from "./App.jsx";
import {Provider} from "react-redux"
import store from "./store/index.js";
const root = ReactDom.createRoot(document.querySelector("#root"));
root.render(
<Provider store={store}>
<App></App>
</Provider>
);
三:通过 react-redux 的 connect
连接组件,将 store 中的指定数据映射过去,作为 组件的 props 属性使用
// About.jsx 组件;该组件是App组件的后代组件
import React, { PureComponent } from 'react'
// 1. 引入 connect 函数
import { connect } from "react-redux"
import { addNumberAction,subNumberAction } from '../store/action'
export class About extends PureComponent {
changeNum(num,isAdd) {
if (isAdd) {
this.props.addNumber(num);
} else {
this.props.subNumber(num);
}
}
render() {
const { count } = this.props;
return (
<div>
<h2>About:{count}</h2>
<button onClick={(e) => this.changeNum(1, true)}>+1</button>
<button onClick={(e) => this.changeNum(1,false)}>-1</button>
</div>
);
}
}
const mapStateToProps = (state) => ({
count: state.count, // 映射 store中的哪些数据
});
const mapDispatchToProps = (dispatch) => ({
// 映射 store 中的哪些 action ,然后 作为组件的 props (如:addNumber)去调用
addNumber(num) {
dispatch(addNumberAction(num));
},
subNumber(num) {
dispatch(subNumberAction(num));
},
});
// connect函数 的返回值是一个高阶函数
// connect 传入两个函数的作为参数
export default connect(mapStateToProps, mapDispatchToProps)(About);
案例描述:Left 和 Right 为App 的 子组件,Left 和 Right 共享 store 中 的 state (一个count);Left 组件能 使 count +1;Right 组件使 count -1;
结构如图:
下面 贴上各部分代码
store/constants.js
// 定义常量 ;action type 的 类型
export const INCREMENT = "INCREMENT";
export const DECREMENT = "DECREMENT";
store/actionCreator.js
import { INCREMENT, DECREMENT } from "./constants"
// 返回 action 的函数;记住 action 是个 对象
export const incrementAction = (num) => {
return {
type: INCREMENT,
num,
}
}
export const decrementAction = (num) => {
return {
type: DECREMENT,
num,
};
};
store/reducer.js
import * as actionTypes from "./constants"
// 创建一个对象,保存状态 state
const initialState = {
count: 100,
};
// 定义 reducer; 接收两个参数(state,action)
function reducer(state = initialState, action) {
const {INCREMENT,DECREMENT} = actionTypes
switch (action.type) {
case INCREMENT:
return { ...state, count: state.count + action.num };
case DECREMENT:
return { ...state, count: state.count - action.num };
default:
return state;
}
}
export default reducer
store/index.js
import { createStore } from "redux"
import reducer from "./reducer";
// 创建 redux store 来存放应用状态;并把 reducer传入
const store = createStore(reducer);
export default store;
pages/Left.jsx
import React, { PureComponent } from 'react'
import {incrementAction} from "../store/actionCreators"
import { connect } from "react-redux"
class Left extends PureComponent {
add(num) {
this.props.onAddClick(num);
}
render() {
const { count } = this.props;
return (
<div>
<h2>Left: {count}</h2>
<button onClick={(e) => this.add(1)}>+1</button>
</div>
);
}
}
// 映射哪些 state 到 组件的 props 中
const mapStateToProps = state => {
return {
count:state.count
}
}
// 映射哪些 action 到 组件的 props中
const mapActionToProps = dispatch => {
return {
onAddClick: num => {
dispatch(incrementAction(num));
}
}
}
export default connect(mapStateToProps, mapActionToProps)(Left);
pages/Right.jsx
import React, { PureComponent } from 'react'
import { connect } from "react-redux";
import {decrementAction} from "../store/actionCreators"
class Right extends PureComponent {
sub(num) {
this.props.onSubClick(num)
}
render() {
const { count } = this.props;
return (
<div>
<h2>Right: { count }</h2>
<button onClick={(e) => this.sub(2)}>-2</button>
</div>
);
}
}
// 映射哪些 state 到 组件的 props 中
const mapStateToProps = state => {
return {
count:state.count
}
}
// 映射哪些 action 到 组件的 props中
const mapActionToProps = dispatch => {
return {
onSubClick: num => {
dispatch(decrementAction(num));
}
}
}
export default connect(mapStateToProps, mapActionToProps)(Right);
import React, { PureComponent } from 'react'
import Left from './pages/Left';
import Right from './pages/Right';
export default class App extends PureComponent {
render() {
return (
<div>
<h2>App</h2>
<Left></Left>
<Right></Right>
</div>
);
}
}
import ReactDom from "react-dom/client";
import App from "./App.jsx";
import { Provider } from "react-redux";
import store from "./store"
const root = ReactDom.createRoot(document.querySelector("#root"));
root.render(
<Provider store={ store }>
<App></App>
</Provider>
);
在上面简单的案例中,redux 中保存的 count 是一个本地定义的数据
网络请求可以在class组件的componentDidMount中发送,所以我们可以有这样的结构:
// 当前位于 Category 组件
import React, { PureComponent } from 'react'
import axios from "axios"
import { connect } from 'react-redux';
import {getRecommend,getBanner} from "../store/action"
class Category extends PureComponent {
componentDidMount() {
axios.get("http://123.207.32.32:8000/home/multidata").then(res => {
// 获取了数据
const banner = res.data.data.banner.list;
const recommends = res.data.data.recommend.list;
// 更新 state;
this.props.changeBanner(banner);
this.props.changeRecommend(recommends);
})
}
render() {
console.log(this.props.banner); // 能拿到 banner
return (
<div>
<h2>Category</h2>
</div>
);
}
}
// 编写 映射的 state 和 action
const mapStateToProps = (state) => {
return {
banner: state.banner,
recommends: state.recommends,
};
}
const mapActionToProps = (dispatch) => {
return {
changeBanner(banner) {
dispatch(getBanner(banner));
},
changeRecommend(recommend) {
dispatch(getRecommend(recommend));
}
}
}
export default connect(mapStateToProps, mapActionToProps)(Category);
// 当前位于 action.js 文件
export const getBanner = (banner) => ({
type: actionTypes.GET_BANNER,
banner
})
export const getRecommend = (recommend) => ({
type: actionTypes.GET_RECOMMEND,
recommend,
});
省略其他文件代码
上面的代码有一个缺陷:
但是在 redux 中如何进行异步的操作呢?
redux 也引入了中间件(Middleware)的概念:
我们现在要做的事情就是发送异步的网络请求,所以我们可以添加对应的中间件:
redux-thunk 是如何做到让我们可以发送异步的请求呢?
1、安装redux-thunk
npm i redux-thunk
2、在创建 store 时传入应用了 middleware 的 enhance
函数
applyMiddleware
来结合多个 Middleware, 返回一个enhancer;3、定义返回一个函数的action:
【示例代码】
// store -> index.js
// 增强 store
import { createStore,applyMiddleware } from "redux";
import thunk from "redux-thunk";
import reducer from "./reducer";
// 这里直接 为 store 通过 applyMiddleware 增加了 thunk 中间件
const store = createStore(reducer,applyMiddleware(thunk));
export default store;
// 在 store->action.js
// 定义 获取网络请求 action
import axios from "axios";
export const getBanner = (banner) => ({
type: actionTypes.GET_BANNER,
banner
})
export const fetchDataAction = () => {
// 如果是一个普通的action,那么这里需要返回 action 对象
// 如果返回的是一个函数,那么 redux是不支持的,需要使用 redux-thunk 库
return function (dispatch, getState) {
// 异步操作: 网络请求
axios.get("http://123.207.32.32:8000/home/multidata").then((res) => {
const banner = res.data.data.banner.list;
dispatch(getBanner(banner));
});
};
}
// 在 组件中,直接调用一个函数,即可完成 异步请求数据到 store 中
import {fetchDataAction} from "../store/action"
class Category extends PureComponent {
componentDidMount() {
this.props.fetchData();
}
render() {
return (
<div>
<h2>Category:{this.props.count}</h2>
</div>
);
}
}
const mapActionToProps = (dispatch) => {
return {
fetchData() {
dispatch(fetchDataAction());
}
};
};
export default connect(null, mapActionToProps)(Category);
redux 可以方便的让我们对状态进行跟踪和调试,那么如何做到呢?
安装该工具需要两步:
import { createStore,applyMiddleware,compose } from "redux";
import thunk from "redux-thunk";
import reducer from "./reducer";
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer, composeEnhancers(applyMiddleware(thunk)));
export default store;
再推荐一个 React Developer Tools 插件,可以检查 React components、编辑 props 和 state
假设 这个 reducer 既有处理 count 的代码,又有处理 home(banner数据) 页面的数据;
因此,我们可以对 reducer 进行拆分:
这里仅展示 count 模块的四个文件代码
1、store -> count>constants.js
export const ADD_NUMBER = "add_number";
export const SUB_NUMBER = "sub_number";
2、store->count->actionCreators.js
import * as actionTypes from "./constants";
export const addNumberAction = (num) => ({
type: actionTypes.ADD_NUMBER, num
});
export const subNumberAction = (num) => ({
type: actionTypes.SUB_NUMBER,
num,
});
3、store->count->reducer.js
import * as actionTypes from "./constants"
const initialState = {
count:100
}
function reducer(state = initialState,action) {
switch (action.type) {
case actionTypes.ADD_NUMBER:
return { ...state, count: state.count + action.num };
case actionTypes.SUB_NUMBER:
return { ...state, count: state.count - action.num };
default:
return state;
}
}
export default reduce
4、store->count->index.js
import reducer from "./reducer"
export default reducer
export * from "./actionCreators"
5、store -> index.js
import { createStore,applyMiddleware,compose,combineReducers } from "redux";
import thunk from "redux-thunk";
import countReducer from "./count"
import homeReducer from "./home"
// 将两个 reducer 合并在一起
const reducer = combineReducers({
count: countReducer,
home: homeReducer,
});
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer, composeEnhancers(applyMiddleware(thunk)));
export default store;
在组件中使用数据时,和之前的区别是,需要多一层指定,来告诉 store ,你需要的是哪个模块的状态 (例如:store.getState().count.count 表示获取 count 模块中 count 数据)
由上述可见,redux 给我们提供了一个 combineReducers 函数可以方便的让我们对多个 reducer 进行合并:
Redux Toolkit 是官方推荐的编写 Redux 逻辑的方法。
安装 Redux Toolkit:
npm install @reduxjs/toolkit react-redux
Redux Toolkit 的核心 API 主要是如下几个:
configureStore
:包装 createStore 以提供简化的配置选项和良好的默认值。它可以自动组合你的 slice reducer,添加你提供的任何 Redux 中间件,redux-thunk 默认包含,并启用 Redux DevTools Extension。createSlice
:接收 reducer 函数的对象、切片名称和初始状态值,并自动生成切片reducer,并带有相应的 actions。createAsyncThunk
: 接收一个动作类型字符串和一个返回承诺的函数,并生成一个 pending/fulfilled/rejected 基于该承诺分派动作类型的 thunk通过 createSlice 创建一个slice。
import { createSlice } from "@reduxjs/toolkit";
const countSlice = createSlice({
name: 'count',
initialState: {
count:100,
},
reducers: {
addCount(state,{payload}) {
state.count = state.count + payload
},
subCount(state, { payload }) {
state.count = state.count - payload;
}
}
})
export const {addCount,subCount} = countSlice.actions
export default countSlice.reducer
createSlice 主要包含如下几个参数:
1、name:用户标记 slice 的名词
2、initialState:第一次初始化时的值;
3、reducers:相当于之前的 reducer 函数
4、createSlice 返回值是一个对象,包含所有的 actions;
configureStore 用于创建 store 对象,常见参数如下:
import { configureStore } from "@reduxjs/toolkit";
import countReducer from "./features/count"
import homeReducer from "./features/home"
const store = configureStore({
reducer: {
count: countReducer,
home: homeReducer,
},
});
export default store
在之前的开发中,我们通过 redux-thunk 中间件让 dispatch 中可以进行异步操作。
Redux Toolkit默认已经给我们集成了 Thunk 相关的功能:createAsyncThunk
当 createAsyncThunk 创建出来的 action 被 dispatch 时,会存在三种状态:
我们可以在 createSlice 的 entraReducer 中监听这些结果:
【示例:home 的 数据】
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const fetchHomeDataAction = createAsyncThunk("fetctHomeData", async (extraInfo,store) => {
// 发送网络请求,获取数据
const res = await axios.get("http://123.207.32.32:8000/home/multidata");
// 这里有两种方式可以改变 store 中 的 数据
// 1.一是直接 通过 store 的 dispatch去修改
// 2.二是通过 return 出去的数据在 extraReducers中,去修改
// const banners = res.data.data.banner.list;
// store.dispatch(changeBanners(banners));
return res.data;
});
const homeSlice = createSlice({
name: "home",
initialState: {
banners: [],
},
reducers: {
changeBanners(state, { payload }) {
state.banners = payload;
},
},
extraReducers: {
[fetchHomeDataAction.pending](state, action) {console.log('pending')},
[fetchHomeDataAction.fulfilled](state, action) {
const banners = action.payload.data.banner.list;
state.banners = banners;
},
[fetchHomeDataAction.rejected](state, action) {console.log('rejected')},
},
});
export const { changeBanners } = homeSlice.actions;
export default homeSlice.reducer;
extraReducer的另外一种写法(了解即可)
extraReducers: (builder) => {
builder
.addCase(fetchHomeDataAction.pending, (state) => {
console.log("fetchHomeDataAction pending");
})
.addCase(fetchHomeDataAction.fulfilled, (state,{payload}) => {
state.banners = payload.data.banner.list;
})
.addCase(fetchHomeDataAction.rejected, (state) => {
console.log("fetchHomeDataAction rejected");
});
}
【补充Home组件】
import React, { PureComponent } from 'react'
import { connect } from "react-redux";
import { subCount } from "../store/features/count"
// import store from '../store';
import {fetchHomeDataAction} from "../store/features/home"
class Home extends PureComponent {
componentDidMount() {
this.props.fetchHomeData();
}
subClick(num) {
this.props.subNumber(num);
// store.dispatch(subCount(num));
// store.dispatch(fetchHomeDataAction());
}
render() {
const { count,banners } = this.props;
return (
<div>
<h2>Home中的count:{count}</h2>
<ul>
{banners.map((item, index) => {
return <li key={index}>{item.title}</li>;
})}
</ul>
<button onClick={(e) => this.subClick(2)}>-2</button>
<hr />
</div>
);
}
}
const mapStateToProps = (state) => {
return {
count: state.count.count,
banners: state.home.banners,
};
}
const mapDispatchToProps = (dispatch) => {
return {
subNumber(num) {
dispatch(subCount(num));
},
fetchHomeData() {
dispatch(fetchHomeDataAction());
}
};
}
export default connect(mapStateToProps,mapDispatchToProps)(Home);
在 React 开发中,我们总是会强调数据的不可变性:
所以在前面我们经常会进行浅拷贝来完成某些操作,但是浅拷贝事实上也是存在问题的:
事实上 Redux Toolkit 底层使用了 immerjs 的一个库来保证数据的不可变性。
为了节约内存,又出现了一个新的算法:Persistent Data Structure(持久化数据结构或一致性数据结构);
案例描述:在Home 组件 App 组件,Profile 组件 共享 state 的count ,Home 组件 的 banner 数据 是异步获取的;
结构如图:
store/features/count.js
import { createSlice } from "@reduxjs/toolkit";
const countSlice = createSlice({
name: 'count',
initialState: {
count:100,
},
reducers: {
addCount(state,{payload}) {
state.count = state.count + payload
},
subCount(state, { payload }) {
state.count = state.count - payload;
}
}
})
export const {addCount,subCount} = countSlice.actions
export default countSlice.reducer
store/features/home.js
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const fetchHomeDataAction = createAsyncThunk("fetctHomeData", async (extraInfo,store) => {
// 发送网络请求,获取数据
const res = await axios.get("http://123.207.32.32:8000/home/multidata");
// 这里有两种方式可以改变 store 中 的 数据
// 1.一是直接 通过 store 的 dispatch去修改
// 2.二是通过 return 出去的数据在 extraReducers中,去修改
// const banners = res.data.data.banner.list;
// store.dispatch(changeBanners(banners));
return res.data;
});
const homeSlice = createSlice({
name: "home",
initialState: {
banners: [],
},
reducers: {
changeBanners(state, { payload }) {
state.banners = payload;
},
},
extraReducers: {
// [fetchHomeDataAction.pending](state, action) {console.log('pending')},
[fetchHomeDataAction.fulfilled](state, action) {
const banners = action.payload.data.banner.list;
state.banners = banners;
},
// [fetchHomeDataAction.rejected](state, action) {console.log('rejected')},
},
// extraReducers: (builder) => {
// builder
// .addCase(fetchHomeDataAction.pending, (state) => {
// console.log("fetchHomeDataAction pending");
// })
// .addCase(fetchHomeDataAction.fulfilled, (state,{payload}) => {
// state.banners = payload.data.banner.list;
// })
// .addCase(fetchHomeDataAction.rejected, (state) => {
// console.log("fetchHomeDataAction rejected");
// });
// }
});
export const { changeBanners } = homeSlice.actions;
export default homeSlice.reducer;
store/index.js
import { configureStore } from "@reduxjs/toolkit";
import countReducer from "./features/count"
import homeReducer from "./features/home"
const store = configureStore({
reducer: {
count: countReducer,
home: homeReducer,
},
});
export default store
pages/Home.jsx
import React, { PureComponent } from 'react'
import { connect } from "react-redux";
import { subCount } from "../store/features/count"
// import store from '../store';
import {fetchHomeDataAction} from "../store/features/home"
class Home extends PureComponent {
componentDidMount() {
this.props.fetchHomeData();
}
subClick(num) {
this.props.subNumber(num);
// store.dispatch(subCount(num));
// store.dispatch(fetchHomeDataAction());
}
render() {
const { count,banners } = this.props;
return (
<div>
<h2>Home中的count:{count}</h2>
<ul>
{banners.map((item, index) => {
return <li key={index}>{item.title}</li>;
})}
</ul>
<button onClick={(e) => this.subClick(2)}>-2</button>
<hr />
</div>
);
}
}
const mapStateToProps = (state) => {
return {
count: state.count.count,
banners: state.home.banners,
};
}
const mapDispatchToProps = (dispatch) => {
return {
subNumber(num) {
dispatch(subCount(num));
},
fetchHomeData() {
dispatch(fetchHomeDataAction());
}
};
}
export default connect(mapStateToProps,mapDispatchToProps)(Home);
pages/Profile.jsx
import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import store from '../store'
import { addCount } from "../store/features/count"
class Profile extends PureComponent {
addCount(num) {
// this.props.addClick(num);
store.dispatch(addCount(num));
}
render() {
const {count} = this.props
return (
<div>
<h2>Profile中的count{count}</h2>
<button onClick={e=>this.addCount(8)}>+8</button>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
count: state.count.count,
}
}
const mapDispatchToProps = (dispatch) => {
return {
// addClick(num) {
// dispatch(addCount(num));
// },
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Profile);
import React, { PureComponent } from 'react'
import Home from './pages/Home';
import Profile from './pages/Profile';
import { connect } from 'react-redux';
class App extends PureComponent {
render() {
const {count} = this.props
return (
<div>
<h2>App: count:{count}</h2>
<hr />
<Home></Home>
<Profile></Profile>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
count:state.count.count
}
}
export default connect(mapStateToProps)(App);
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import store from './store';
import { Provider } from 'react-redux';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={ store }>
<App />
</Provider>
);
前面学习了 Redux 用来管理我们的应用状态,并且非常好用
目前我们已经主要学习了三种状态管理方式:
在开发中如何选择
目前项目中推荐采用的 state 管理方案: