我正在使用 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 中做到这一点吗?
这可能不是答案,但我这样使用它:
const isLoggedIn = useSelector<IRootState, boolean>(state => state.user.loggedIn);
编辑:或者使用Peter的答案,它更短/更干净
const isLoggedIn = useSelector((state: IRootState) => state.user.loggedIn);
您可以像这样创建自定义类型的 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
。
这是来自 redux 文档的建议(或多或少):
import { RootState } from 'app/redux/store';
const isLoggedIn = useSelector(state: RootState => state.user.loggedIn);
相对于@Federkun 的答案的优点是它更简单。相对于 @alextrastero 的答案的优点是我不必手动指定 isLoggedIn 的类型。
根据 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);
config.d.ts
import 'react-redux';
import { ApplicationState } from '@store/index';
declare module 'react-redux' {
interface DefaultRootState extends ApplicationState {}
}
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
我认为这是当今最好、最简单的方法。 希望它对某人有用。
感谢
npm install --save typesafe-actions
转到您的减速器或 rootReducer 文件并
import { StateType } from 'typesafe-actions';
写出你的减速函数并且
export type Store = StateType<typeof yourReducer>;
转到您要使用的 ts 文件,然后
import { useSelector } from 'react-redux';
import {Store} from 'your-reducer-or-wherever-file'
在你的组件内部:
const store = useSelector((store: Store) => { return {students: store.manager.students} });
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
}
export interface IUser {
userName: string;
}
export interface IRootState {
user: IUser;
}
const user = useSelector<IRootState, IUser>(state => state.user);
我认为彻底理解这一点的最好方法是 Redux 文档本身。
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;
我刚刚在代码中找到了这个片段
/**
* 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() 时将其用作状态类型
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)
您可以只使用任何类型。 只是另一个解决方案。