【问题标题】:React-Redux useSelector typescript type for stateReact-Redux useSelector typescript type for state
【发布时间】:2019-12-19 16:15:43
【问题描述】:

我正在使用来自 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


【解决方案1】:

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

const isLoggedIn = useSelector&lt;IRootState, boolean&gt;(state =&gt; state.user.loggedIn);

编辑:或者使用更短/更干净的Peter's answer

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

【讨论】:

  • 这是正确的答案。各位,不要再点赞上面的答案,而是点赞这个。
  • 是的。为我工作。谢谢!
  • 对上面的陈述忍不住笑了。很好的答案,它也有帮助
  • 什么是 IRootState?
  • @TuanDo,IRootState 是你的应用状态的接口,在我的例子中是:js import { combineReducers } from 'redux'; const rootReducer = combineReducers({ user: userReducer, foo: fooReducer, }) export type IRootState = ReturnType&lt;typeof rootReducer&gt;;
【解决方案2】:

您可以像这样创建自定义类型的 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>

这是definitely typed declaration中描述的方法。

别忘了安装@types/react-redux

【讨论】:

  • Type '{ navDrawer: Reducer&lt;NavDrawerInitialState, AnyAction&gt;; }' does not satisfy the constraint '(...args: any) =&gt; any'. Type '{ navDrawer: Reducer&lt;NavDrawerInitialState, AnyAction&gt;; }' provides no match for the signature '(...args: any): any'.
  • 哪里会出现这个错误,涉及的代码是什么?似乎与TypedUseSelectorHook 无关
  • const state = useSelector( (getState: ReturnType&lt;typeof reducer&gt;) =&gt; getState.navDrawer );
  • @santamanno,谢谢你让我知道,我附上了一张便条,将任何人重定向到 NearHuscarl 的答案
  • @phry 感谢您的纠正。我已经删除了我的答案。
【解决方案3】:

这是来自the redux docs 的建议(或多或少):

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

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

【讨论】:

  • 简单实用!谢谢!
  • 工作得非常好。谢谢
  • 我还要提到它需要安装@types/react-redux(从今天开始)
【解决方案4】:
  1. 创建config.d.ts

  2. 定义您的自定义状态

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

【讨论】:

  • 这应该是实际选择的答案。
  • 这个解决方案就像一个魅力,但有一个小问题是ApplicationState 的代码完成没有带入DefaultRootState,你如何解决这个问题?
【解决方案5】:

根据 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); 

【讨论】:

    【解决方案6】:

    感谢@Иван Яковлев,我终于实现了我的完美解决方案:D

    import { useSelector } from 'react-redux'
    import configureStore from '../configureStore'
    
    export const { store } = configureStore()
    // We can use RootState type in every file in project
    declare global {
      type RootState = ReturnType<typeof store.getState>
    }
    
    // Thanks to that you will have ability to use useSelector hook with state value
    declare module 'react-redux' {
      interface DefaultRootState extends RootState { }
    }
    
    const profile = useSelector(state => state.profile) // Profile is correctly typed
    

    【讨论】:

      【解决方案7】:

      使用类型安全操作。它真的让你的生活变得轻松。

      1. npm install --save typesafe-actions
      2. 转到您的 reducer 或 rootReducer 文件并
      3. import { StateType } from 'typesafe-actions';
      4. 编写你的 reducer 函数并
      5. export type Store = StateType&lt;typeof yourReducer&gt;;

      然后

      1. 转到您要使用的 ts 文件并
      2. import { useSelector } from 'react-redux';
      3. import {Store} from 'your-reducer-or-wherever-file'
      4. 在您的组件内部:
      5. const store = useSelector((store: Store) =&gt; { return {students: store.manager.students} });

      请注意我是如何在 useSelector 钩子中使用从 reducer 导出的 Store 类型(您的 store 类型是 useSelector 需要的,您可以像我们刚才所做的那样通过 typesafe-actions 轻松获得它)。 另外,请注意我如何返回一个包含我想要使用的所有状态的对象。你可以在这一点上发挥创意,没关系。因此,第 10 行中的 store 变量包含所有状态,您也可以根据需要进行解构。 您可以从https://www.npmjs.com/package/typesafe-actions 阅读更多关于类型安全操作的信息

      【讨论】:

        【解决方案8】:
        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
        }
        

        【讨论】:

          【解决方案9】:

          'react-redux' 包提供了一种类型

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

          【讨论】:

            【解决方案10】:

            我刚刚在代码中发现了这个sn-p

            /**
             * 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 {}
            

            【讨论】:

              【解决方案11】:

              你可以使用 any 作为类型。 只是另一种解决方案。

              【讨论】:

                【解决方案12】:

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

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

                他们说

                定义类型挂钩​ 虽然可以将 RootState 和 AppDispatch 类型导入每个组件,但最好创建 useDispatch 和 useSelector 挂钩的预键入版本以供在应用程序中使用。这很重要,有几个原因: 对于useSelector,它省去了你每次输入(状态: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;
                

                【讨论】:

                  猜你喜欢
                  • 2020-05-17
                  • 2021-09-03
                  • 1970-01-01
                  • 2022-12-01
                  • 2020-12-06
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2022-12-09
                  相关资源
                  最近更新 更多