【React】React Redux

210 阅读3分钟

安装

安装 Redux ToolkitReact Redux

npm install @reduxjs/toolkit react-redux

基本使用

创建Store

src/store/index.ts 文件中导入 configureStore 创建一个 Redux Store

// store/index.ts
import { configureStore } from '@reduxjs/toolkit';
export default configureStore({
  reducer: {},
});

创建Provider

在src/index.tsx中引入创建的store,在<App> 的外层放置一个 <Provider>,并将 store 作为 prop 传递

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import store from './store';
import { Provider } from 'react-redux';

// 从 React 18 开始
const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(
  <Provider store={store}>
    <App />
  </Provider>
);

创建State Slice

import { createSlice } from "@reduxjs/toolkit";

interface CounterState {
  valuenumber;
}

const initialStateCounterState = {
  value0
}; 

export const counterSlice = createSlice({
  name"counter",
  initialState,
  reducers: {
    increment(state) => {
      state.value += 1;
    },
    decrement(state) => {
      state.value -= 1;
    },
    incrementByAmount(state, action: PayloadAction<number>) => {
      state.value += action.payload;
    }
  }
});

export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;

State Slice Reducer 添加到store

import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./reducers/counter";

const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export default store;

使用Reducer

引入 useSelector 获取counter reducer中的state,引入 dispatch 触发counter reducer 中事件改变state数据状态。

import { useSelector, useDispatch } from "react-redux";
import { increment, decrement } from "../../store/reducers/counter";

const Home = () => {
  const count = useSelector((state: any) => state.counter.value);
  const dispatch = useDispatch();
  
  const addNumber1 = () => {
    dispatch(increment());
  };
  
  const addNumber2 = () => {
    dispatch({ type"counter/increment" });
  };
 
  const decrementNumber1 = () => {
    dispatch(decrement());
  };
  
  const decrementNumber2 = () => {
    dispatch({ type"counter/decrement" });
  };
  return (
    <div>
      <h2>Home</h2>
      <div>
        <div>{count}</div>
        <button onClick={addNumber1}>Add Number</button>
        <button onClick={addNumber2}>Add Number</button>
        <hr />
        <button onClick={decrementNumber1}>Decrement Number</button>
        <button onClick={decrementNumber2}>Decrement Number</button>
      </div>
    </div>
  )
}

export default Home;

优化调用

上面使用 reducer 的过程无法使用类型约束,如需使用类型约束需要将 RootStateAppDispatch 类型导入每个组件过程有些繁琐,可以进行全局封装

import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import type { AppDispatchRootState } from "../store";

// useDispatch
export const useAppDispatch: () => AppDispatch = useDispatch;
// useSelector

export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

// store根状态
export type AppRootState = RootState;

优化后的使用方式有类型提示,使用过程更友好

import { increment, decrement } from "../../store/reducers/counter";
import { useAppDispatch, useAppSelector } from '../../hooks'

const Home = () => {
  const count = useAppSelector((state) => state.counter.value);
  const dispatch = useAppDispatch();
 
  const addNumber1 = () => {
    dispatch(increment());
  };
 
  const addNumber2 = () => {
    dispatch({ type"counter/increment" });
  };

  const decrementNumber1 = () => {
    dispatch(decrement());
  };

  const decrementNumber2 = () => {
    dispatch({ type"counter/decrement" });
  };
 
  return (
    <div>
      <h2>Home</h2>
      <div>
        <div>{count}</div>
        <button onClick={addNumber1}>Add Number</button>
        <button onClick={addNumber2}>Add Number</button>
        <hr />
        <button onClick={decrementNumber1}>Decrement Number</button>
        <button onClick={decrementNumber2}>Decrement Number</button>
      </div>
    </div>
  )
}

export default Home;

Connect高级组件类型

语法

// redux state
const mapState = (state: RootState) => ({
  isOn: state.isOn,
});

// redux actions
const mapDispatch = {
  toggleOn: () => ({ type'TOGGLE_IS_ON' }),
};

// 自定义类型
interface OwnProps {}

// 组合props类型
type Props = ReturnType<typeof StateProps> & ReturnType<typeof DispatchProps> & OwnProps;

const MyComponent = (props: Props) => {
    return <></>
};

export default connect<StateProps, DispatchProps, OwnProps>(
  mapState,
  mapDispatch
)(MyComponent);

connect 由两个按顺序调用的函数组成:

  • 第一个函数接受 mapStatemapDispatch 作为参数(可选参数),并返回第二个函数
  • 第二个函数接受要被包裹的组件,并返回一个新的包裹组件,该组件将来自 mapStatemapDispatchprops 向下传递。这两个函数会一起调用,例如 connect(mapState, mapDispatch)(MyComponent)

mapStateToProps

mapStateToProps 函数应该返回一个包含组件所需数据的普通对象:

  • 对象中的每个字段都将成为实际组件中的 prop
  • 字段中的值将用于确定你的组件是否需要重新渲染
import { connect } from "react-redux";
import { AppRootState } from '../../hooks'

const mapStateToProps = (rootState: AppRootState) => {
    return {
      counterState: rootState.counter,
    }
};

// 简写
//const mapStateToProps = (rootState: AppRootState) => ({
//   counterState: rootState.counter,
//});

type IProps = ReturnType<typeof mapStateToProps>;

const Home = (props: IProps) => {
  console.log("Home", props.counterState);
  return (
    <div>
      <h2>Home</h2>
      <div>{props.counterState.value}</div>
    </div>
  )
}

export default connect(mapStateToProps)(Home);

mapStateToProps 函数的第一个参数是整个 Redux store state,第二个参数 ownProps (可选的)通常用来利用props检索store中的数据。

interface IProps {
  id?: number;
}

const mapStateToProps = (rootState: AppRootState, ownProps: IProps) => {
  // 组件自定义props
  console.log('ownProps',ownProps);
  return {
    counterState: rootState.counter,
  };
};

// 组件使用
<Home id={1} />

mapDispatchToProps

import type { AppRootStateAppDispatchType, } from '../../hooks'
import { connect, ConnectedProps } from "react-redux";

interface IProps {
  name?: string
}

const mapDispatchToProps = (dispatch: AppDispatchType) => {
  return {
    increment() => dispatch({ type'counter/increment' }),
    decrement() => dispatch({ type'counter/decrement' })
  }
}

type Props = ReturnType<typeof mapStateToProps> & IProps;

const About = (props: Props) => {
  return (
    <div>
      <h2>{props.count}</h2>
      <h2 onClick={props.increment}>About</h2>
    </div>
  )
}

export default connect(null, mapDispatchToProps)(About);

mapDispatchToProps 函数的第一个参数是整个 Redux store actions,第二个参数 ownProps (可选的)通常用来利用props变更时更新store中的数据。

import type { AppRootStateAppDispatchType, } from '../../hooks'
import { connect, ConnectedProps } from "react-redux";

interface IProps {
  name?: string
}

const mapDispatchToProps = (dispatch: AppDispatchType, ownProps: IProps) => {
  console.log('ownProps',ownProps)
  return {
    increment() => dispatch({ type'counter/increment' }),
    decrement() => dispatch({ type'counter/decrement' })
  }
}

type Props = ReturnType<typeof mapStateToProps> & IProps;

const About = (props: Props) => {
  return (
    <div>
      <h2>{props.count}</h2>
      <h2 onClick={props.increment}>About</h2>
    </div>
  )
}

export default connect(null, mapDispatchToProps)(About);

自动类型推断

借助 ConnectedProps _可以实现_自动类型推断,减少类型约束。

import { connect, ConnectedProps } from "react-redux";
import type { AppRootStateAppDispatchType } from '../../hooks'

const mapStateToProps = (rootState: AppRootState) => ({
  counterState: rootState.counter,
});

const mapDispatchToProps = (dispatch: AppDispatchType) => ({
  addNumber() => dispatch(increment()),
});

// 传统写法
// type IProps = ReturnType<typeof mapStateToProps> & ReturnType<typeof mapDispatchToProps>;

// 优化后的写法
const connector = connect(mapStateToProps, mapDispatchToProps);

type IProps = ConnectedProps<typeof connector>;

const Home = (props: IProps) => {
  return (
    <div>
      <h2>Home</h2>
    </div>
  )
}

export default connector(Home);

友情提示

见原文:【React】React Redux)

本文同步自微信公众号 "程序员小溪" ,这里只是同步,想看及时消息请移步我的公众号,不定时更新我的学习经验。