React-Redux useSelector typescript type for state

问题描述 投票:0回答:15

我正在使用 React-Redux 中的

useSelector(state => state.SLICE_NAME)
钩子,但是我在定义
state
参数时遇到困难。它默认设置为
unknown
,因此当我尝试返回
state.SLICE_NAME
(
Error: Object is of type 'unknown'
) 时会收到错误。

如何定义

state
类型,而无需手动创建单独的状态类型并在创建时添加每个新的状态定义?

我尝试将状态定义为

typeof store
但这不起作用。

一些帮助解释的代码:

// navDrawer.ts

import { createSlice } from "redux-starter-kit";

// navDrawer initial state
export interface NavDrawerInitialState {
  open: boolean;
}

const navDrawerInitialState: NavDrawerInitialState = {
  open: false
};

// Create state slice
const { actions, reducer } = createSlice({
  slice: "navDrawer",
  initialState: navDrawerInitialState,
  reducers: {
    open: (state: NavDrawerInitialState) => {
      state.open = true;
    },
    close: (state: NavDrawerInitialState) => {
      state.open = false;
    }
  }
});

export const navDrawerActions = actions;
export default reducer;
// reducers.ts

import navDrawer from "./navDrawer";

const reducers = {
  navDrawer
};

export default reducers;
// store.ts

import { configureStore } from "redux-starter-kit";
import reducer from "./reducers";

const store = configureStore({
  reducer
});

export default store;
// Page.tsx

import React, { FC } from "react";
import { Provider } from "react-redux";
import store from "./store";
import ChildComponent from "./ChildComponent";

const StateProvider: FC = () => {
  return <Provider store={store}><ChildComponent /></Provider>;
};

export default StateProvider;
// ChildComponent.tsx

import React, { FC } from "react";
import { useSelector } from "react-redux";

const ChildComponent: FC = () => {
  const navDrawerState = useSelector(state => state.navDrawer); // ERROR OCCURS HERE. "state" is defined as 'unknown' so "state.navDrawer" throws an error.
  return <div>Text</div>
}

编辑:我注意到

configureStore()
的类型定义包含状态作为第一个泛型类型。请参阅下面的屏幕截图。如果我可以从
EnhancedStore
获取第一个通用值,那么我将能够使用它来定义状态。有什么办法可以在 Typescript 中做到这一点吗?

typescript redux react-redux
15个回答
123
投票

这可能不是答案,但我这样使用它:

const isLoggedIn = useSelector<IRootState, boolean>(state => state.user.loggedIn);

编辑:或者使用Peter的答案,它更短/更干净

const isLoggedIn = useSelector((state: IRootState) => state.user.loggedIn);

58
投票

您可以像这样创建自定义类型的 useSelector :

import {
  useSelector as useReduxSelector,
  TypedUseSelectorHook,
} from 'react-redux'
import { RootState } from 'app/redux/store'

export const useSelector: TypedUseSelectorHook<RootState> = useReduxSelector

其中

RootState
是商店的类型,通常定义为:

export type RootState = ReturnType<typeof rootReducer>

这是明确类型声明中描述的方法。

不要忘记安装

@types/react-redux


41
投票

这是来自 redux 文档的建议(或多或少):

import { RootState } from 'app/redux/store';
const isLoggedIn = useSelector(state: RootState => state.user.loggedIn);

相对于@Federkun 的答案的优点是它更简单。相对于 @alextrastero 的答案的优点是我不必手动指定 isLoggedIn 的类型。


36
投票

根据 Redux 文档,将状态导出为 store.tsx 文件中的 RootState

// Infer the `RootState` and `AppDispatch` types from the store itself export type RootState = ReturnType<typeof store.getState>
然后在组件中将其用作

const navDrawerOpen = useSelector((state:RootState) => state.navDrawer.open);
    

14
投票
  1. 创建

    config.d.ts

    
    

  2. 定义您的自定义状态

    import 'react-redux'; import { ApplicationState } from '@store/index'; declare module 'react-redux' { interface DefaultRootState extends ApplicationState {} }
    
    

6
投票
在此视频中,Mark Erikson 展示了如何创建一些自定义 useAppSelector 和 useAppDispatch,以便为它们提供正确的类型。 hooks.ts 中的示例:

import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'; import { RootState, AppDispatch } from './store'; export const useAppDispatch = () => useDispatch<AppDispatch>(); export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

然后在 store.ts 中我们有:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';

import productsReducer from '../slices/products.slice';

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

export default store;

export type AppDispatch = typeof store.dispatch; // Here we export the store's dispatch type
export type RootState = ReturnType<typeof store.getState>; // Here we export the store's state

我认为这是当今最好、最简单的方法。 希望它对某人有用。
    

感谢


3
投票

npm install --save typesafe-actions
  1. 转到您的减速器或 rootReducer 文件并
  2. import { StateType } from 'typesafe-actions';
  3. 写出你的减速函数并且
  4. export type Store = StateType<typeof yourReducer>;
  5. 然后

转到您要使用的 ts 文件,然后

  1. import { useSelector } from 'react-redux';
  2. import {Store} from 'your-reducer-or-wherever-file'
  3. 在你的组件内部:
  4. const store = useSelector((store: Store) => { return {students: store.manager.students} });
  5. 请注意我如何在 useSelector 挂钩中使用从减速器导出的 Store 类型(您的 store 类型是 useSelector 需要的,您可以像我们刚才那样使用 typesafe-actions 轻松获取它)。
    另外,请注意我如何返回一个包含我想要使用的所有状态的对象。此时你可以发挥创意,这并不重要。因此,第 10 行中的 store 变量具有所有状态,如果需要,您也可以直接解构。
    您可以从 
  6. https://www.npmjs.com/package/typesafe-actions

阅读有关 typesafe-actions 的更多信息

“react-redux”包提供了一种类型

3
投票
import { RootStateOrAny} from "react-redux"; const recentActivityResponse = useSelector( (state: RootStateOrAny) => state.dashboard.recentActivityResponse );

import React from 'react'
import { useSelector } from 'react-redux'

type RootState = {
    auth: {
        login: string
        isAuth: boolean
    }
}

const State: RootState = {
  auth: {
     login: ''
     isAuth: false
  }
}

export function useSelectorTyped<T>(fn: (state: RootState) => T): T {
  return useSelector(fn)
}

const LoginForm = () => {
    const { login, loginError } = useSelectorTyped(state => state.auth)
    return null
}

2
投票
试试这个:

2
投票
export interface IUser { userName: string; } export interface IRootState { user: IUser; } const user = useSelector<IRootState, IUser>(state => state.user);

我认为彻底理解这一点的最好方法是 Redux 文档本身。

1
投票

https://react-redux.js.org/using-react-redux/usage-with-typescript

他们说

定义类型化挂钩 虽然可以将 RootState 和 AppDispatch 类型导入到每个组件中,但最好创建 useDispatch 和 useSelector 挂钩的预键入版本以便在应用程序中使用。这很重要,原因如下: 对于useSelector来说,它省去了你每次都输入(state: RootState)的需要 对于 useDispatch,默认的 Dispatch 类型不知道 thunk 或其他中间件。为了正确分派 thunk,您需要使用商店中包含 thunk 中间件类型的特定自定义 AppDispatch 类型,并将其与 useDispatch 一起使用。添加预先键入的 useDispatch 挂钩可以防止您忘记在需要的地方导入 AppDispatch。

因此,下面的方法应该可以解决问题,并且始终用于避免不断键入选择器或调度。

export type RootState = ReturnType<typeof store.getState>; export type AppDispatch = typeof store.dispatch; export const useAppDispatch = () => useDispatch<AppDispatch>(); export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

我刚刚在代码中找到了这个片段

0
投票
/** * This interface can be augmented by users to add default types for the root state when * using `react-redux`. * Use module augmentation to append your own type definition in a your_custom_type.d.ts file. * https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation */ // tslint:disable-next-line:no-empty-interface export interface DefaultRootState {}

您应该在 store.ts 中创建一个接口,其中包含所有文件的所有状态值的所有类型,然后将其导入并在使用 useState() 时将其用作状态类型

0
投票
在 foo.ts

import { createSlice } from "@reduxjs/toolkit" const foo = createSlice({ initialState: {value: "some value"} }) export default foo.reducer

在 store.ts
    import {foo} from "./foo.ts"
    import {configureStore} from "@reduxjs/toolkit"
    interface RootState {
      foo: {
        value: string
      }
    }
    
    export default configureStore({
        reducer: {foo: foo}
    })

在目标文件
import { RootState } from "../store"
import { useState } from "react-redux"
const bar = useState((state: RootState) => state.foo.value)

您可以只使用任何类型。
只是另一个解决方案。

-3
投票

© www.soinside.com 2019 - 2024. All rights reserved.