TS2339: Property 'tsReducer' does not exist on type 'DefaultRootState'
Solution 1
It is complaining about the type. Quick solution would be adding any
as state type.
Proper solution will require following two steps:
- Create RootState type in Root Reducer.
export const rootReducer = combineReducers({
dashboard: dashboardReducer,
user: userReducer
});
export type RootState = ReturnType<typeof rootReducer>
- Provide RootState type to state object.
let userData = useSelector((state: RootState) => {
return state.user.data;
});
Solution 2
You need to declare the type of the state
argument in your selector, like:
const isDialogOpen = useSelector( (state: RootState) => state.tsReducer.isDialogOpen);
Please see the Redux docs on TypeScript usage, as well as the React-Redux docs page on static typing for examples.
(Also, as a stylistic note: please don't call that tsReducer
in your root state. Give it a name that matches the data it's handling, like state.ui
.)
Solution 3
For me, a better solution than specifying state in useSelector would be as below.
As in the node_modules/@types/react-redux/index.d.ts
, you can use module augmentation.
/**
* 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 {}
Do as below
- Export AppState in reducer
src/reducer/index.ts
const reducers = combineReducers({
userReducer,
});
export type AppState = ReturnType<typeof reducers>;
- Create new
your_custom_type.d.ts
. (I prefer react-redux.d.ts).
Insrc/@types/your_custom_type.d.ts
import 'react-redux';
import { AppState } from '../reducers';
declare module 'react-redux' {
interface DefaultRootState extends AppState { };
}
- Add
typeRoots
in tsconfig.json
{
"compilerOptions": {
...
"typeRoots": ["src/@types"]
}
}
You can use as below without specifying AppState
import React, { memo } from 'react';
import { useSelector } from 'react-redux';
export default memo(() => {
const isLoggedIn = useSelector(
({ userReducer }) => userReducer.isLoggedIn
);
return <div>{isLoggedIn}</div>;
});
Solution 4
If you are using react-redux, another out of the box solution would be to use RootStateOrAny
.
import { RootStateOrAny, useSelector } from 'react-redux';
// and then use it like so in your component
...
const authState = useSelector((state: RootStateOrAny) => state.auth);
...
Solution 5
These are useful articles.
- https://redux.js.org/recipes/usage-with-typescript#define-root-state-and-dispatch-types
- https://redux.js.org/recipes/usage-with-typescript#define-typed-hooks
So first define RootState
and AppDispatch
like following:
//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
And then define hooks (useAppDispatch
, useAppSelector
) can be used in components.
//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
And use it on component like following:
import {useAppSelector} from '../store/hooks'
//...
const TsApp = (): JSX.Element => {
const dispatch = useDispatch();
// ERROR should be fixed
const isDialogOpen = useAppSelector(state => state.tsReducer.isDialogOpen);
};
Ry2254
Updated on July 09, 2022Comments
-
Ry2254 almost 2 years
Struggling with the above question. Seen similar questions but cannot figure it out.
The below code is me attempting to open and close a dialog using TypeScript for first time in an existing React project which uses .js and .jsx.
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;
import {ActionTypes} from './types';
export interface TsDialogAction { isDialogOpen: boolean number: number }
export interface CloseTsDialog { type: ActionTypes.closeDialog payload: TsDialogAction }
export interface OpenTsDialog { type: ActionTypes.openDialog payload: TsDialogAction }
export interface Increment { type: ActionTypes.increment payload: TsDialogAction }
export interface Decrement { type: ActionTypes.decrement payload: 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});
-
Ry2254 about 4 yearsThanks for taking the time to answer the question. However, I am still seeing the same error.
-
markerikson about 4 yearsCan you update the question with the actual type declaration code you're trying to use?
-
Ry2254 about 4 yearsupdated question as requested but wasn't allow to put it in
code
-
Priyanshu Sekhar over 3 yearsElegant solution !
-
Muganwas about 3 yearsThis seems to be the best answer, thank you @Yuvraj
-
Erkka Mutanen about 3 yearsGreat answer and this solution is tidy and it works.
-
Quinton Chester almost 3 yearsFantastic solution. Thanks for sharing... way cleaner than importing AppState on every usage. 👍
-
Mohammad Ahmad about 2 yearsGreat solution. Actually, this could work with any other libraries too. Brilliant