【问题标题】:redux reducer type 'never'redux 减速器类型“从不”
【发布时间】:2020-06-13 17:27:44
【问题描述】:

我想使用 'useSelector' 选择正确的 rootStore 状态,但无法正确获取状态。原因是 RootState 的主题缩减器给了我类型 never

export type RootState = ReturnType<typeof rootReducer>

我怎样才能正确输入topic

整个代码看起来像

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

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



【问题讨论】:

  • 添加useSelector 相关sn-p

标签: reactjs typescript redux


【解决方案1】:

解决方案

您可以使用redux 库中的Reducer 类型来键入您的reducers。将topicReducer 的类型更改为:

import { Reducer } from 'redux';

const topicReducer: Reducer<TopicBasicState,TopicAction> = (
    state = initialState, action
) => {

这应该为您提供RootState 的以下输入:

type RootState = {
    readonly [$CombinedState]?: undefined;
} & {
    topic: TopicBasicState;
}

来源:

【讨论】:

  • 此解决方案有效。我想知道这个问题是由打字稿版本引起的吗?我看到很多关于 redux 和 typescript 的例子并没有指定每个 reducers
  • 打字稿版本是^3.9.3
【解决方案2】:

已经很糟糕了RootState解析为{topic: never}类型。

可能是redux 打字,typescript 版本不匹配。

尝试为 rootReducer 显式设置类型

heres the definition

export const rootReducer: Reducer<{
 topic: TopicBasicState
}> = combineReducers({
  topic
});

const topic = useSelector<RootState, TopicName>(state => state.topic);

【讨论】:

  • 感谢您的回答,但不幸的是,这不起作用。无论如何谢谢。
猜你喜欢
  • 1970-01-01
  • 2019-06-06
  • 2021-12-30
  • 1970-01-01
  • 1970-01-01
  • 2019-11-26
  • 2018-02-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多