React-Redux useSelector TypeScript类型用于状态

时间:2019-08-13 06:18:15

标签: typescript redux react-redux

我正在使用React-Redux的useSelector(state => state.SLICE_NAME)钩子,但是我很难定义state参数。默认情况下将其设置为unknown,因此当我尝试返回state.SLICE_NAMEError: 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中做到这一点?

enter image description here

9 个答案:

答案 0 :(得分:39)

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

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

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

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

答案 1 :(得分:13)

以下是the redux docs中的建议(或多或少):

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

与@Federkun的答案相比,优点是简单得多。与@alextrastero的答案相比,优点是我不必手动指定isLoggedIn的类型。

答案 2 :(得分:9)

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

答案 3 :(得分:3)

  1. 创建config.d.ts

  2. 定义您的自定义状态

    declare module 'react-redux' {
      interface DefaultRootState extends YourAppState {}
    } 
    

答案 4 :(得分:3)

我真的很喜欢Federkun的answer,因为您可以一次定义一个包装器钩子,然后在其他任何地方重用,但是不推荐使用TypedUseSelectorHook,所以这里是更新的答案:

import { createSelectorHook } from "react-redux"

type YourRootState = {
  // ...
}

export const useSelector = createSelectorHook<YourRootState>()

用法

// import your custom hook useSelector

// state is YourRootState now
const item = useSelector((state) => state.your.item)

实时演示

Edit 57472105/react-redux-useselector-typescript-type-for-state

答案 5 :(得分:1)

使用typesafe-actions。这确实使您的生活变得轻松。

  1. npm install --save typesafe-actions
  2. 转到您的reducer或rootReducer文件,然后
  3. import { StateType } from 'typesafe-actions';
  4. 编写减速器功能,然后
  5. export type Store = StateType<typeof yourReducer>;

然后

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

请注意我如何在useSelector挂钩中使用从我的reducer导出的商店类型(商店的类型是useSelector所需要的,您可以像我们刚才那样通过typesafe-actions轻松获得它)。 另外,请注意我如何返回包含要使用的所有状态的对象。此时您可以发挥创意,没关系。因此,第10行中的store变量具有所有状态,并且您也可以根据需要进行分解。 您可以从https://www.npmjs.com/package/typesafe-actions

了解有关类型安全操作的更多信息

答案 6 :(得分:0)

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
}

答案 7 :(得分: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 {}

答案 8 :(得分:0)

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