TS2339:类型“ DefaultRootState”上不存在属性“ tsReducer”

时间:2020-03-20 16:17:52

标签: reactjs typescript redux

正在努力解决上述问题。看到类似的问题,但无法解决。

以下代码是我尝试在使用.js和.jsx的现有React项目中首次使用TypeScript打开和关闭对话框。

import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import {useDispatch, useSelector} from 'react-redux';
import {closeTsDialog} from '../actions/tsDialog'
import {ActionTypes} from '../actions/types';

const TsApp = (): JSX.Element => {
    const dispatch = useDispatch();

// ERROR SHOWS UP ON LINE BELOW "state?.tsReducer?.isDialogOpen"
    const isDialogOpen = useSelector(state => state?.tsReducer?.isDialogOpen);
    const state = useSelector(s => s);
    console.log('->>>>>> state', state);


    // main tsx excluded to allow for posting on stackoverflow
};


export default TsApp;
import {TsDialogAction} from "../actions/tsDialog";


const initialState = {
    id: 0,
    isDialogOpen: false
};

const tsReducer = (state: TsDialogAction = initialState, action: Action) => {
    switch (action.type) {
        case ActionTypes.closeDialog: {
            return {...state, isDialogOpen: false};
        }
        case ActionTypes.openDialog: {
            return {...state, isDialogOpen: true};
        }
        default:
            return state;
    }
};

export default tsReducer;

从'./types'导入{ActionTypes};

导出接口TsDialogAction { isDialogOpen:布尔值 号:号 }

导出界面CloseTsDialog { 类型:ActionTypes.closeDialog 有效负载:TsDialogAction }

导出界面OpenTsDialog { 类型:ActionTypes.openDialog 有效负载:TsDialogAction }

导出界面增量{ 类型:ActionTypes.increment 有效负载:TsDialogAction }

导出界面减量{ 类型:ActionTypes.decrement 有效负载:TsDialogAction }

export const closeTsDialog =(id:number)=>({type:ActionTypes.closeDialog,payload:id}); export const openTsDialog =(id:number)=>({type:ActionTypes.openDialog,payload:id}); export constcrementAction =(id:number)=>({type:ActionTypes.increment,有效负载:id}); export const decrementAction =(id:number)=>({type:ActionTypes.decrement,有效载荷:id});

5 个答案:

答案 0 :(得分:14)

您需要在选择器中声明state参数的类型,例如:

const isDialogOpen = useSelector( (state: RootState) => state.tsReducer.isDialogOpen);

有关示例,请参见Redux docs on TypeScript usageReact-Redux docs page on static typing

(此外,作为风格上的注释:请不要在您的根状态下调用tsReducer。为其命名,以匹配要处理的数据,例如state.ui。)

答案 1 :(得分:8)

它在抱怨类型。快速解决方案是将any添加为状态类型。

正确的解决方案将需要执行以下两个步骤:

  1. 在Root Reducer中创建RootState类型。
export const rootReducer = combineReducers({
  dashboard: dashboardReducer,
  user: userReducer
});

export type RootState = ReturnType<typeof rootReducer>
  1. 为状态对象提供RootState类型。
  let userData = useSelector((state: RootState) => {
    return state.user.data;
  });

答案 2 :(得分:7)

如果您使用的是 react-redux,另一种开箱即用的解决方案是使用 RootStateOrAny

import { RootStateOrAny, useSelector } from 'react-redux';

// and then use it like so in your component
...
const authState = useSelector((state: RootStateOrAny) => state.auth);
...


答案 3 :(得分:5)

对我来说,比在useSelector中指定状态更好的解决方案如下。
node_modules/@types/react-redux/index.d.ts中一样,您可以使用模块扩充。

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

执行以下操作

  1. 在减速器src/reducer/index.ts中导出AppState
const reducers = combineReducers({
  userReducer,
});

export type AppState = ReturnType<typeof reducers>;
  1. 创建新的your_custom_type.d.ts。 (我更喜欢react-redux.d.ts)。
    src/@types/your_custom_type.d.ts
import 'react-redux';

import { AppState } from '../reducers';

declare module 'react-redux' {
  interface DefaultRootState extends AppState { };
}
  1. 在tsconfig.json中添加typeRoots
{
  "compilerOptions": {
    ...
    "typeRoots": ["src/@types"]
  }
}

您可以按以下方式使用,而无需指定AppState

import React, { memo } from 'react';
import { useSelector } from 'react-redux';

export default memo(() => {
  const isLoggedIn = useSelector(
    ({ userReducer }) => userReducer.isLoggedIn
  );
  return <div>{isLoggedIn}</div>;
});

答案 4 :(得分:2)

这些都是有用的文章。

  1. https://redux.js.org/recipes/usage-with-typescript#define-root-state-and-dispatch-types
  2. https://redux.js.org/recipes/usage-with-typescript#define-typed-hooks

所以首先定义 RootStateAppDispatch 如下:

//at store/index.ts
const rootReducer = combineReducers({
  tsReducer: tsReducer
});
const store = createStore(rootReducer)

export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch

然后定义钩子(useAppDispatchuseAppSelector)可以在组件中使用。

//at store/hooks.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
import type { RootState, AppDispatch } from './'

export const useAppDispatch = () => useDispatch<AppDispatch>()
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector

并在组件上使用它,如下所示:

import {useAppSelector} from '../store/hooks'
//...
const TsApp = (): JSX.Element => {
    const dispatch = useDispatch();

// ERROR should be fixed
    const isDialogOpen = useAppSelector(state => state.tsReducer.isDialogOpen);
};