I want to use 'useSelector' to select proper state of rootStore, but can't get state properly. The reason is topic reducer of RootState gives me type never
export type RootState = ReturnType<typeof rootReducer>
How can I get topic type properly?
the entire codes look like
///topic.ts
import { Action, ActionCreator } from 'redux';
import { CardData } from '@models/card';
import { TopicName } from 'src/models/topic';
import { ActionEnum } from './index';
//action
export enum TopicEnum {
FETCH_TOPIC = "FETCH_TOPIC"
}
type TopicType = ActionEnum | TopicEnum;
export interface TopicBasicState{
topic: TopicName
isLoading :boolean
data: CardData[]
isError: boolean
}
export const initialState: TopicBasicState = {
topic: 'all',
isLoading: true,
data: [] as CardData[],
isError: false
}
//action type
export interface FetchAction extends Action {
type: typeof TopicEnum.FETCH_TOPIC
url: string;
}
interface LoadingAction extends Action{
type: typeof ActionEnum.FETCH_LOADING
}
interface SuccessAction extends Action{
type: typeof ActionEnum.FETCH_SUCCESS
payload: TopicBasicState
}
interface ErrorAction extends Action{
type: typeof ActionEnum.FETCH_ERROR
}
//action creator
const fetch: ActionCreator<FetchAction> = (
url
) => ({
type: TopicEnum.FETCH_TOPIC,
url,
});
const load: ActionCreator<LoadingAction> = () => ({
type: ActionEnum.FETCH_LOADING,
});
const success: ActionCreator<SuccessAction> = (_, payload) => ({
type: ActionEnum.FETCH_SUCCESS,
payload
})
const error: ActionCreator<ErrorAction> = () => ({
type: ActionEnum.FETCH_ERROR
})
type TopicAction =
| ReturnType<typeof fetch>
| ReturnType<typeof load>
| ReturnType<typeof success>
| ReturnType<typeof error>
export const topicCreator = {
fetch,
load,
success,
error
}
const topicReducer: (
state: TopicBasicState,
action: TopicAction
) => TopicBasicState = (
state = initialState, action
) => {
switch(action.type){
case TopicEnum.FETCH_TOPIC:
return {
...state,
isError: false
}
case ActionEnum.FETCH_SUCCESS:
return {
...state,
topic: action.payload?.topic,
isLoading: false,
data: action.payload?.data
}
case ActionEnum.FETCH_LOADING:
return {
...state,
isLoading: true,
}
case ActionEnum.FETCH_ERROR:
return {
...state,
isLoading: false,
isError: true
}
default:
return state;
}
}
export type TopicState = ReturnType<typeof topicReducer>;
export default topicReducer;
** store/index.ts **
import React, { FC } from 'react';
import { Provider } from 'react-redux';
import {
combineReducers,
createStore,
applyMiddleware,
compose
} from 'redux';
import createSagaMiddleware from 'redux-saga';
import topic from './topic';
import { rootSaga } from '@sagas/index';
export enum ActionEnum {
FETCH_LOADING = "FETCH_LOADING",
FETCH_SUCCESS = "FETCH_SUCCESS",
FETCH_ERROR = "FETCH_ERROR"
}
export type DefaultAction =
| ActionEnum.FETCH_SUCCESS
| ActionEnum.FETCH_LOADING
| ActionEnum.FETCH_ERROR
// root reducer
export const rootReducer = combineReducers({
topic
});
// root store type
export type RootState = ReturnType<typeof rootReducer>
...
const { data, isLoading, id } = useSelector((state: RootState) => state.topic);
const dispatch= useDispatch();
const { fetch} = topicCreator;
useEffect(() => {
console.log('path: ', pathname);
const [ include, exclude ] = pathExtractor(pathname);
dispatch(fetch());
},[])
const handleClick : (data: CardData ) => void = (data) => {
setModal({
type: 'OPEN',
payload: {
data,
visible: true
}
});
}
return(
<>
<section>
<Carousel>
<Slide url={Netflix} />
<Slide url={Adobe} />
<Slide url={NetflixPhone} />
</Carousel>
</section>
<section>
{isModal.visible && <Modal data={isModal.data} />}
</section>
<section>
<RoomContainer>
{
isLoading ?
<ProgressBar/>
:
data.map((value) => { // <-- this variable data cause error because typescript cannot infer data as array but type never
return <Card
data={value}
key={value.id}
handleClick={() => handleClick(value)}
/>
})
}
</RoomContainer>
</section>
</>
)
});
You can use the Reducer
type from the redux
library to type your reducers. Change the typing of topicReducer
to the following:
import { Reducer } from 'redux';
const topicReducer: Reducer<TopicBasicState,TopicAction> = (
state = initialState, action
) => {
That should give you the following typing for RootState
:
type RootState = {
readonly [$CombinedState]?: undefined;
} & {
topic: TopicBasicState;
}
Reducer
typeIt is already bad RootState
resolved to {topic: never}
type.
Perhaps it's redux
typings, typescript
version mismatch.
Try to set explicitly type for rootReducer
heres the definition
export const rootReducer: Reducer<{
topic: TopicBasicState
}> = combineReducers({
topic
});
const topic = useSelector<RootState, TopicName>(state => state.topic);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With