Luchando con la pregunta anterior. He visto preguntas similares pero no puedo resolverlo.
El siguiente código es mío intentando abrir y cerrar un cuadro de diálogo usando TypeScript por primera vez en un proyecto React existente que usa .js y .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;importar {ActionTypes} desde './types';
interfaz de exportación TsDialogAction {isDialogOpen: número booleano: número}
interfaz de exportación CloseTsDialog { tipo: ActionTypes.closeDialog carga útil: TsDialogAction }
interfaz de exportación OpenTsDialog { tipo: ActionTypes.openDialog carga útil: TsDialogAction }
interfaz de exportación Incremento { tipo: ActionTypes.increment payload: TsDialogAction }
interfaz de exportación Decremento { tipo: ActionTypes.decrement payload: TsDialogAction }
exportar const closeTsDialog = (id: número) => ({tipo: ActionTypes.closeDialog, payload: id}); exportar const openTsDialog = (id: número) => ({tipo: ActionTypes.openDialog, payload: id}); export const incrementAction = (id: número) => ({tipo: ActionTypes.increment, payload: id}); export const decrementAction = (id: número) => ({tipo: ActionTypes.decrement, payload: id});
Debe declarar el tipo de argumento de state en su selector, como:
const isDialogOpen = useSelector( (state: RootState) => state.tsReducer.isDialogOpen);Consulte los documentos de Redux sobre el uso de TypeScript , así como la página de documentos de React-Redux sobre escritura estática para ver ejemplos.
(Además, como nota estilística: no lo llame tsReducer en su estado raíz. Déle un nombre que coincida con los datos que está manejando, como state.ui ).
Se queja del tipo. La solución rápida sería agregar any como tipo de estado.
La solución adecuada requerirá los siguientes dos pasos:
export const rootReducer = combineReducers({ dashboard: dashboardReducer, user: userReducer }); export type RootState = ReturnType<typeof rootReducer> let userData = useSelector((state: RootState) => { return state.user.data; });Para mí, una mejor solución que especificar el estado en useSelector sería la siguiente.
Al igual que en node_modules/@types/react-redux/index.d.ts , puede usar el aumento de módulos.
/** * 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 {}Haz lo siguiente
src/reducer/index.ts const reducers = combineReducers({ userReducer, }); export type AppState = ReturnType<typeof reducers>;your_custom_type.d.ts . (Prefiero reaccionar-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 { }; }typeRoots en tsconfig.json { "compilerOptions": { ... "typeRoots": ["src/@types"] } }Puede usar como se muestra a continuación sin especificar AppState
import React, { memo } from 'react'; import { useSelector } from 'react-redux'; export default memo(() => { const isLoggedIn = useSelector( ({ userReducer }) => userReducer.isLoggedIn ); return <div>{isLoggedIn}</div>; });Si está utilizando react-redux, otra solución lista para usar sería usar RootStateOrAny .
import { RootStateOrAny, useSelector } from 'react-redux'; // and then use it like so in your component ... const authState = useSelector((state: RootStateOrAny) => state.auth); ...Estos son artículos útiles.
Así que primero defina RootState y AppDispatch como sigue:
//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 Y luego definir ganchos ( useAppDispatch , useAppSelector ) que se pueden usar en componentes.
//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> = useSelectorY utilícelo en un componente como el siguiente:
import {useAppSelector} from '../store/hooks' //... const TsApp = (): JSX.Element => { const dispatch = useDispatch(); // ERROR should be fixed const isDialogOpen = useAppSelector(state => state.tsReducer.isDialogOpen); };esto funcionó para mí
import { RootStateOrAny, useSelector } from "react-redux" const search = useSelector((state: RootStateOrAny) => state.searchObj.value)se trata solo de los tipos que necesita para satisfacer este gancho, useSelector acepta el tipo DefaultRootState , puede anular sus tipos predeterminados a través de este
type User = { firstname: string; lastname: string; }; type SelectorTypes = { users: User[]; //...other reducers }; const router = useSelector<SelectorTypes>((state) => state.users);