React18中使用Redux Toolkit,RTK基本使用,RTK异步操作,手写connect,provider

616 阅读3分钟

认识Redux Toolkit

创建新项目:

  • create-react-app 009learn-reduxtoolkit
  • npm i @reduxjs/toolkit react-redux image.png

RTK基本使用

createSlice

image.png

configureStore

image.png

代码: 从下至上

image.png

.panel {
  display: flex;
}
.panel > div {
  flex: 1;
  padding: 20px;
  border: 2px solid magenta;
}
import ReactDOM from "react-dom/client";
import App from "./App";
import { Provider } from "react-redux";
import store from "./store";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <Provider store={store}>
    <App />
  </Provider>
);
import React, { PureComponent } from "react";
import Home from "./pages/Home";
import Profile from "./pages/Profile";
import "./style.css";
import { connect } from "react-redux";
export class App extends PureComponent {
  render() {
    const { count } = this.props;
    return (
      <div>
        <div>App count:{count}</div>
        <div className="panel">
          <Home />
          <Profile />
        </div>
      </div>
    );
  }
}
const mapStateToProps = (state) => ({
  count: state.count.count,
});
export default connect(mapStateToProps, null)(App);
import { configureStore } from "@reduxjs/toolkit";
import countReducer from "./features/count";
const store = configureStore({
  reducer: {
    count: countReducer,
  },
});
export default store;
import { createSlice } from "@reduxjs/toolkit";
const countSlice = createSlice({
  name: "count",
  initialState: {
    count: 999,
  },
  reducers: {
    addCount(state, { payload }) {
      state.count += payload;
    },
    subCount(state, { payload }) {
      state.count -= payload;
    },
  },
});
export const { addCount, subCount } = countSlice.actions;
export default countSlice.reducer;
import React, { PureComponent } from "react";
import { connect } from "react-redux";
import { subCount } from "../store/features/count";
export class Profile extends PureComponent {
  render() {
    const { count } = this.props;
    return (
      <div>
        Profile count:{count}
        <div>
          <button onClick={(e) => this.props.subCount(1)}>-1</button>
        </div>
      </div>
    );
  }
}
const mapStateToProps = (state) => ({
  count: state.count.count,
});
const mapDispatchToProps = (dispatch) => ({
  subCount: (count) => dispatch(subCount(count)),
});
export default connect(mapStateToProps, mapDispatchToProps)(Profile);
import React, { PureComponent } from "react";
import { connect } from "react-redux";
import { addCount } from "../store/features/count";
export class Home extends PureComponent {
  render() {
    const { count } = this.props;
    return (
      <div>
        Home count:{count}
        <div>
          <button onClick={(e) => this.props.addCount(1)}>+1</button>
        </div>
      </div>
    );
  }
}
const mapStateToProps = (state) => ({
  count: state.count.count,
});
const mapDispatchToProps = (dispatch) => ({
  addCount: (count) => dispatch(addCount(count)),
});
export default connect(mapStateToProps, mapDispatchToProps)(Home);

RTK异步操作

  • const asyncAction = createAsyncThunk('actionName',async()=>{})
  • 三种状态(pending,fulfilled,rejected)
  • extraReducers

image.png

RTK其它两个用法

  • extraReducers:(builder)=>{builder.addCase}
  • createAsyncThunk('name',async (extraInfo,store)=>{})

image.png

RTK的数据不可变性

image.png

代码从上到下:

image.png

Home页面获取数据,存储到redux,在Profile组件展示数据

import React, { PureComponent } from "react";
// import { connect } from "react-redux";
import { connect } from "../hoc";
import { addCount } from "../store/features/count";
import { fetchDataAction } from "../store/features/home";
export class Home extends PureComponent {
  componentDidMount() {
    this.props.fetchData();
  }
  render() {
    const { count } = this.props;
    return (
      <div>
        Home count:{count}
        <div>
          <button onClick={(e) => this.props.addCount(1)}>+1</button>
        </div>
      </div>
    );
  }
}
const mapStateToProps = (state) => ({
  count: state.count.count,
});
const mapDispatchToProps = (dispatch) => ({
  addCount: (count) => dispatch(addCount(count)),
  fetchData: () => dispatch(fetchDataAction({ name: "zm", age: 21 })),
});
export default connect(mapStateToProps, mapDispatchToProps)(Home);
import React, { PureComponent } from "react";
// import { connect } from "react-redux";
import { connect } from "../hoc";
import { subCount } from "../store/features/count";
export class Profile extends PureComponent {
  render() {
    const { count, banners, recommends } = this.props;
    return (
      <div>
        Profile count:{count}
        <div>
          <button onClick={(e) => this.props.subCount(1)}>-1</button>
        </div>
        <div>
          <h2>轮播图数据</h2>
          <ul>
            {banners.map((item, index) => {
              return <li key={index}>{item.title}</li>;
            })}
          </ul>
          <h2>推荐数据</h2>
          <ul>
            {recommends.map((item, index) => {
              return <li key={index}>{item.title}</li>;
            })}
          </ul>
        </div>
      </div>
    );
  }
}
const mapStateToProps = (state) => ({
  count: state.count.count,
  banners: state.home.banners,
  recommends: state.home.recommends,
});
const mapDispatchToProps = (dispatch) => ({
  subCount: (count) => dispatch(subCount(count)),
});
export default connect(mapStateToProps, mapDispatchToProps)(Profile);
import { createSlice } from "@reduxjs/toolkit";
const countSlice = createSlice({
  name: "count",
  initialState: {
    count: 999,
  },
  reducers: {
    addCount(state, { payload }) {
      state.count += payload;
    },
    subCount(state, { payload }) {
      state.count -= payload;
    },
  },
});
export const { addCount, subCount } = countSlice.actions;
export default countSlice.reducer;
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import axios from "axios";
export const fetchDataAction = createAsyncThunk("fetch/homedata", async (extraInfo, { dispatch, getState }) => {
  console.log("extraInfo,dispatch,getState=> ", extraInfo, dispatch, getState);
  // 1.发送网络请求,获取数据
  const res = await axios.get("http://123.207.32.32:8000/home/multidata");
  console.log("res.data=> ", res.data);
  //   2.取出数据,并且在此处直接dispatch操作(可选)
  //   const banners = res.data.data.banner.list;
  //   const recommends = res.data.data.recommend.list;
  //   dispatch(changeBanners(banners));
  //   dispatch(changeRecommends(recommends));
  // 3.返回结果,那么action状态会变成fulfilled状态
  return res.data;
});
const homeSlice = createSlice({
  name: "home",
  initialState: {
    banners: [],
    recommends: [],
  },
  reducers: {
    // 同步
    changeBanners(state, { payload }) {
      state.banners = payload;
    },
    changeRecommends(state, { payload }) {
      state.recommends = payload;
    },
  },
  //   extraReducers: {
  //     [fetchDataAction.pending](state, action) {
  //       console.log("fetchDataAction pending");
  //     },
  //     [fetchDataAction.fulfilled](state, { payload }) {
  //       console.log("payload是fetchDataAction()返回值=> ", fetchDataAction(), payload);
  //       console.log("fetchDataAction fulfilled");
  //       state.banners = payload.data.banner.list;
  //       state.recommends = payload.data.recommend.list;
  //     },
  //     [fetchDataAction.rejected](state, action) {
  //       console.log("fetchDataAction rejected");
  //     },
  //   },
  extraReducers: (builder) => {
    // 异步
    builder
      .addCase(fetchDataAction.pending, (state, action) => {
        console.log("fetchDataAction pending");
      })
      .addCase(fetchDataAction.fulfilled, (state, { payload }) => {
        state.banners = payload.data.banner.list;
        state.recommends = payload.data.recommend.list;
      });
  },
});
export const { changeBanners, changeRecommends } = homeSlice.actions;
export default homeSlice.reducer;
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;
import React, { PureComponent } from "react";
import Home from "./pages/Home";
import Profile from "./pages/Profile";
import "./style.css";
import { connect } from "react-redux";
export class App extends PureComponent {
  render() {
    const { count } = this.props;
    return (
      <div>
        <div>App count:{count}</div>
        <div className="panel">
          <Home />
          <Profile />
        </div>
      </div>
    );
  }
}
const mapStateToProps = (state) => ({
  count: state.count.count,
});
export default connect(mapStateToProps, null)(App);
import ReactDOM from "react-dom/client";
import App from "./App";
import { Provider } from "react-redux";
import store from "./store";
import { StoreContext } from "./hoc/StoreContext";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <Provider store={store}>
    <StoreContext.Provider value={store}>
      <App />
    </StoreContext.Provider>
  </Provider>
);

图片:

image.png

手写connect,provider

image.png

import { PureComponent } from "react";
// import store from "../store";
import { StoreContext } from "./StoreContext";
/**
 *
 * @param {函数} mapStateToProps
 * @param {函数} mapDispatchToProps
 * @returns 函数=>高阶组件
 */
export function connect(mapStateToProps, mapDispatchToProps) {
  //返回高级组件:函数
  return function (OriginCpn) {
    class NewCpn extends PureComponent {
      constructor(props, context) {
        super(props);
        // this.state = mapStateToProps(store.getState());
        this.state = mapStateToProps(context.getState());
      }
      componentDidMount() {
        this.unsubscribe = this.context.subscribe(() => {
          // this.forceUpdate()
          this.setState(mapStateToProps(this.context.getState()));
        });
      }
      componentWillUnmount() {
        this.unsubscribe();
      }
      render() {
        const stateObj = mapStateToProps(this.context.getState());
        const dispatchObj = mapDispatchToProps(this.context.dispatch);
        return <OriginCpn {...this.props} {...stateObj} {...dispatchObj} />;
      }
    }
    NewCpn.contextType = StoreContext;
    return NewCpn;
  };
}
export { connect } from "./connect";
export { StoreContext } from "./StoreContext";
import { createContext } from "react";
export const StoreContext = createContext();
import ReactDOM from "react-dom/client";
import App from "./App";
import { Provider } from "react-redux";
import store from "./store";
import { StoreContext } from "./hoc/StoreContext";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <Provider store={store}>
    <StoreContext.Provider value={store}>
      <App />
    </StoreContext.Provider>
  </Provider>
);