React 项目中使用 TypeScript - Redux 基本使用

本文详细介绍了在 TypeScript 项目中使用 React 和 Redux 的实践,包括 Redux 的基本设置、useSelector 的泛型使用、获取 Redux 状态类型、reducer 函数的类型约束、常量断言在行动元(action)中的应用、使用 useDispatch、为 React 事件对象指定类型、redux-thunk 的使用及其新版本特性。通过这些内容,读者可以深入理解如何在 TypeScript 环境下构建和类型化 Redux 应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1. Redux 基本使用

2. useSelector 的使用

3. 获取 Redux 仓库的状态类型 

4. reducer 函数的类型

5. TS 类型-常量断言

6. useDispatch 的使用 

7. React 事件对象的类型

8. redux-thunk 的使用

9. redux-thunk 新版本特性

1. Redux 基本使用

目标:能够掌握如何在 TS 项目中初始化 redux

内容:

  • 安装依赖包:yarn add redux react-redux redux-devtools-extension redux-thunk

  • 新建文件 store/index.ts( 后缀为 .ts )

import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import reducer from './reducers';

const store = createStore(reducer, composeWithDevTools(applyMiddleware(thunk)));

export default store;
  • 新建文件 store/reducers/index.ts
import { combineReducers } from 'redux';

import { todos } from './todos';

const rootReducer = combineReducers({
  todos,
});
export default rootReducer;
  • 新建文件 store/reducers/todos.ts
type TodoList = {
  id: number;
  text: string;
  done: boolean;
};
const initialState: TodoList = [
  {
    id: 1,
    text: '吃饭',
    done: false,
  },
  {
    id: 2,
    text: '睡觉',
    done: true,
  },
  {
    id: 3,
    text: '打豆豆',
    done: false,
  },
];

export const todos = (state = initialState, action: any) => {
  return state;
};
  • index.tsx 中
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';

import App from './App';
import store from './store';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root'),
);

2. useSelector 的使用

目标:能够掌握 useSelector 在 TS 中的使用

内容

  • useSelector hook 是一个泛型函数,接收两个类型变量,分别来指定:

    1. 第一个类型变量:指定 Redux 仓库 state 的类型

    2. 第二个类型变量:指定要获取状态的类型

// useSelector 类型,源码如下:
// TState = DefaultRootState 是 泛型参数 的默认值,设置默认值后,将来在调用该函数时,可以省略该泛型参数不写
export function useSelector<TState = DefaultRootState, TSelected = unknown>(
	// 第一个参数:回调函数,用来获取 redux 状态的回调函数,通过回调函数的返回值来指定要获取的状态
  selector: (state: TState) => TSelected,
  // 第二个参数:可以拿到更新前后的两次状态,通过返回的布尔值就可以来知道状态是否发生变化
  equalityFn?: (left: TSelected, right: TSelected) => boolean
): TSelected;

useSelector 的两种使用方式:

1.指定泛型类型:

// 比如,可以这样调用:
type RootState = { count: number };
const count = useSelector<RootState, number>((state) => state.count);

2.不指定泛型类型,只指定回调函数参数 state 的类型 

type RootState = { count: number };
const count = useSelector((state: RootState) => state.count);

3. 获取 Redux 仓库的状态类型 

目标:能够获取 Redux 仓库的状态类型 

内容:

// store/index.ts 中:

// 获取 Redux 整个仓库的状态类型:
export type RootState = ReturnType<typeof store.getState>;
  • ReturnType 是一个泛型工具类型,用来获取函数的返回值类型
function add(n1: number, n2: number): number {
  return n1 + n2;
}

// 获取函数 add 的类型
type AddFn = typeof add;

// 获取函数 add 的返回值类型
type AddFnReturnType = ReturnType<AddFn>;

// 直接获取 add 函数的返回值类型
type AddFnReturnType = ReturnType<typeof add>;

使用 useSelector hook 获取 Redux 状态: 

import { RootState } from '../store';

// 获取todos数据
const todos = useSelector((state: RootState) => state.todos);

4. reducer 函数的类型

目标:能够掌握 reducers 在 TS 中的写法

内容

action 的类型有两种实现方式:1 自己创建 action 的类型 2 根据 action creator 来得到(扩展)

1.手动创建 action 的类型 

  • 先创建 action 类型,然后,用 action 类型来约束 action creator
type AddTodo = {
  type: 'todos/add';
  payload: string;
};
type DelTodo = {
  type: 'todos/del';
  payload: number;
};
export type TodoAction = AddTodo | DelTodo;

// 添加任务
export const addTodo = (name: string): AddTodo => ({
  type: 'todos/add',
  payload: name,
});

// 删除任务
export const delTodo = (id: number): DelTodo => ({
  type: 'todos/del',
  payload: id,
});

2.根据 action creator 来得到(扩展) 

  • 先创建 action creator,然后,根据 action creator 得到 action 类型
// 1 添加任务
export const addTodo = (text: string) => ({
  // as const 常量断言,会让当前类型固定为 字面量本身,而不再被扩大类型
  //  比如,string 就是 字面量类型 'todos/add' 的扩大类型
  // 如果不加 as const,type 被推断为: string
  // 如果加了 as const,type 被推断为: 'todos/add'
  type: 'todos/add' as const,
  payload: text,
});

// 2 删除任务
export const delTodo = (id: number) => ({
  type: 'todos/del' as const,
  payload: id,
});

// 3 切换任务完成状态
export const toggleTodo = (id: number) => ({
  type: 'todos/toggle' as const,
  payload: id,
});

// 思路2:根据 action creator 来得到 action 的类型
type AddTodo = ReturnType<typeof addTodo>;
type DelTodo = ReturnType<typeof delTodo>;
type ToggleTodo = ReturnType<typeof toggleTodo>;

export type TodoAction = AddTodo | DelTodo | ToggleTodo;
  • 指定 reducer 的 action 参数和返回值的类型
    • 约定:明确为 reducer 指定返回值类型,来约束 return 的内容必须满足返回值类型的要求,防止返回错误的数据
import { TodoAction } from '../actions/todos';

export const todos = (state = initValue, action: TodoAction): TodoList => {
  switch (action.type) {
    // 此时,就会有明确的 action 类型提示了
    case 'todos/add':
      const id = state.length === 0 ? 1 : state[state.length - 1].id + 1;
      return [
        ...state,
        {
          id,
          text: action.payload,
          done: false,
        },
      ];
    case 'todos/del':
      return state.filter((item) => item.id !== action.payload);
    default:
      return state;
  }
};

5. TS 类型-常量断言

目标:能够理解 TS 中的常量断言 as const

内容

  • as const 文档
  • as const 也是一种类型断言,主要用于字面量类型,来得到字面量类型而不是其扩大后的类型,规则如下:
    • 简单类型的字面量,类型变为字面量本身,比如:不会再将字符串值 "hello" 推断为 string 类型
    • 对象字面量的属性变为 readonly
    • 数组字面量变为 readonly 元组
// x 的类型是: string
let x = 'hello';

// 使用 const 断言后,x 的类型是: "hello" 字面量类型
let x = 'hello' as const;

// y 的类型是: { readonly text: "hello" }
let y = { text: 'hello' } as const;

// z 的类型是: readonly [10, 20]
let z = [10, 20] as const;

使用场景:使用 as const 来让 action creator 省略返回值类型 

export const addTodo = (text: string) =>
  ({
    type: 'todos/add' as const,
    payload: text,
  } as const);

// 或:
export const addTodo = (name: string) => ({
  type: 'ADD_TODO' as const,
  payload: name,
});

6. useDispatch 的使用 

目标:能够掌握 useDispatch 在 TS 中的使用

内容:

const dispatch = useDispatch()

<button onClick={() => dispatch(delTodo(item.id))}>x</button>

7. React 事件对象的类型

目标:能够掌握如何在 TS 中为事件对象指定类型

内容:

为 JSX 标签绑定事件时,可能需要指定事件对象的类型,分两种情况:

1.直接在 JSX 标签上写事件处理程序,此时,不需要手动指定事件对象的类型

  • 技巧:在 JSX 标签上先把事件处理程序写好,然后,鼠标移动到事件对象上面,来查看事件对象的类型

2.如果将事件处理程序抽离出来,需要手动指定函数参数(事件对象)的类型

const add = (e: React.KeyboardEvent<HTMLInputElement>) => {
  if (e.code === 'Enter') {
    dispatch(addTodo(name));
    setName('');
  }
};

8. redux-thunk 的使用

目标:能够掌握 redux-thunk 在 TS 中的使用

内容:

指定 redux-thunk 的 action 类型

thunk action 的类型处理:

1.创建 thunk action 的类型:RootThunkAction

import { ThunkAction } from 'redux-thunk';

// 第一个类型参数:thunk action 返回值类型
// 第二个类型参数:Redux 状态的类型
// 第三个类型参数:thunk action 额外参数的类型
// 第四个类型参数:Redux 中所有 action 的类型
export type RootThunkAction = ThunkAction<void, RootState, unknown, TodoAction>;

// 第1、3个类型参数,参照上述文档来指定即可

2.使用 thunk action 类型:将该类型作为 thunk action 的返回值类型

// 将删除任务的 action 修改为 thunk action
// 注意:返回的函数,才是 thunk action
//			delTodo 是 action creator
export const delTodo = (id: number): RootThunkAction => {
  return (dispatch, getState) => {
    setTimeout(() => {
      dispatch({
        type: 'todos/del',
        payload: id,
      });
    }, 1000);
  };
};

9. redux-thunk 新版本特性

注意:redux-thunk@2.4.0 新版使用 TS 重写了框架源码,并且相关的 TS 类型也做了一些调整,变化如下:

1.在 redux-thunk@2.3.0 版本中,在 thunk action 中分发对象 action 时有明确的类型提示

export const delTodo = (id: number): RootThunkAction => {
  return (dispatch) => {
    setTimeout(() => {
      // 此处,在写 对象action 时,输入 type 属性会有明确的代码提示
      dispatch({
        type: 'todos/del',
        payload: id,
      });
    }, 1000);
  };
};

2.在 redux-thunk@2.4.0 新版本中,上述操作没有了明确的类型提示

所以,如果想要在 thunk action 中 diapatch 对象 action 时有类型提示,可以安装 2.3.0 版本:yarn add redux-thunk@2.3.0 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值