【问题标题】:TS2339: Property 'tsReducer' does not exist on type 'DefaultRootState'TS2339:“DefaultRootState”类型上不存在属性“tsReducer”
【发布时间】:2020-07-01 19:24:49
【问题描述】:

在上面的问题上苦苦挣扎。看到类似的问题,但无法弄清楚。

下面的代码是我第一次尝试在使用 .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 const incrementAction = (id: number) => ({type: ActionTypes.increment, payload: id}); export const decrementAction = (id: number) => ({type: ActionTypes.decrement, payload: id});

【问题讨论】:

    标签: reactjs typescript redux


    【解决方案1】:

    这些都是有用的文章。

    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
    

    然后定义可以在组件中使用的钩子(useAppDispatch,useAppSelector)。

    //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);
    };
    

    【讨论】:

      【解决方案2】:

      如果您使用的是 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】:

        对我来说,比在 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. 在 reducer 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>;
        });
        

        【讨论】:

        • 优雅的解决方案!
        • 很好的答案,这个解决方案很整洁而且很有效。
        • 很棒的解决方案。感谢分享...比每次使用时都导入 AppState 更干净。 ?
        【解决方案4】:

        它在抱怨类型。快速解决方案是将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;
          });
        

        【讨论】:

        • 这似乎是最好的答案,谢谢@Yuvraj
        【解决方案5】:

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

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

        请参阅Redux docs on TypeScript usageReact-Redux docs page on static typing 获取示例。

        (另外,作为一种风格说明:请不要在根状态下调用 tsReducer。给它一个与其正在处理的数据匹配的名称,例如 state.ui。)

        【讨论】:

        • 感谢您抽出宝贵时间回答问题。但是,我仍然看到同样的错误。
        • 您能用您尝试使用的实际类型声明代码更新问题吗?
        • 按要求更新了问题,但不允许将其放入code
        猜你喜欢
        • 1970-01-01
        • 2021-08-21
        • 2020-11-24
        • 2016-03-14
        • 2017-02-25
        • 2021-11-24
        • 2019-01-11
        • 2021-05-08
        • 2021-10-15
        相关资源
        最近更新 更多