使用 UseReducer 和 useContext 钩子进行分派:类型 {} 上不存在分派

时间:2021-05-18 21:06:06

标签: reactjs typescript use-context use-reducer createcontext

我正在尝试使用 React 构建一个小型 Cesium 应用程序,该应用程序可以通过单击与每个位置关联的按钮缩放到地球上的各个位置。我正在使用 useContext 和 useReducer 钩子设置 reducer,但是我在调​​度方面遇到了问题,我无法弄清楚并且到目前为止还没有找到任何指向我的问题的东西可能。到目前为止,我看过的所有页面都告诉我,我所拥有的应该可以工作,但显然我遗漏了一些东西。

以下是我正在处理的两个文件:

location-card.tsx

export function LocationCard(props: LocationCardProps) {
  const classes = useStyles();
  const { className, name, type, location, onDetails } = props;

  const { dispatch } = useMapViewerContext();

  return (
    <Card variant="outlined" className={classes.locationCard}>
      <div className={classes.locationDetails}>
        <CardContent className={classes.locationContent}>
          <Typography component="div" variant="subtitle2">
            {name}
          </Typography>
          <Typography variant="caption" color="textSecondary">
            {type}
          </Typography>
        </CardContent>
        <div className={classes.locationCardControls}>
          <Tooltip title="Zoom to Location">
            <IconButton
              aria-label="Zoom To Location"
              onClick={() => {dispatch(zoomToLocation(location))}}
              className={classes.locationButton}
            >
.....

上述文件还有更多内容,但这是相关的部分。我得到的错误是我尝试设置调度的地方。我被告知

<块引用>

类型“{}”上不存在属性“dispatch”

ma​​p-viewer-context.tsx

import React, { useContext, useReducer, useState } from 'react';
import Cartesian3 from 'cesium/Source/Core/Cartesian3';
import { Context } from 'node:vm';

export interface MapViewerState {
  location: Cartesian3;
  isCameraFlyTo: boolean;
  zoomToLocation: (value: Cartesian3) => void;
}

const initialState: MapViewerState = {
  location: Cartesian3.fromDegrees(
    -95.96044633079181,
    41.139682398924556,
    2100
  ),
  isCameraFlyTo: false,
  zoomToLocation: undefined,
};

export const MapViewerContext = React.createContext();

// Actions
export const ZOOM_TO_LOCATION = "ZOOM_TO_LOCATION";
export const COMPLETE_ZOOM = "FINISH";

// Action creators
export const zoomToLocation = (value: Cartesian3) => {
  return { type: ZOOM_TO_LOCATION, value };
}

// Reducer
export const mapViewerReducer = (state, action): MapViewerState => {
  switch (action.type) {
    case ZOOM_TO_LOCATION:
      return {...state, location: action.value, isCameraFlyTo: true};
    case COMPLETE_ZOOM:
      return {...state, isCameraFlyTo: false}
    default:
      return state;
  }
}

const MapViewerProvider = (props) => {
  const [state, dispatch] = useReducer(mapViewerReducer, initialState);

  const mapViewerData = { state, dispatch };

  return <MapViewerContext.Provider value={mapViewerData} {...props} />;
};

const useMapViewerContext = () => {
  return useContext(MapViewerContext);
};

export { MapViewerProvider, useMapViewerContext };

这里是我设置操作、reducer 和上下文的地方。我也有一个问题,让 React.createContext() 没有参数。我可以通过为它提供我定义的 initialState 来消除该错误,但是 location-card.tsx 中的错误只是更改为:

<块引用>

“MapViewerState”类型不存在“dispatch”属性。

谁能帮我弄清楚如何让它工作?到目前为止,我所尝试的一切都失败了。

2 个答案:

答案 0 :(得分:1)

您看到的错误是由 createContext 未正确推断您的上下文类型引起的。您可以像这样明确指定上下文的类型:

export interface MVContext {
  state: MapViewerState;
  dispatch: (action: MapViewerAction) => void;
}

export const MapViewerContext = createContext<MVContext>({} as MVContext);

只要您不打算在上下文提供程序之外使用 {} as MVContext,类型强制 useContext 就是安全的。如果是,则需要为每个键提供默认值:

export const MapViewerContext = createContext<MVContext>({
  state: MapViewerState.Default,
  dispatch: () => {},
});

答案 1 :(得分:0)

您为 map-view-context 编写的代码很有趣。我把它提取出来让我通读一遍。

1 export const MapViewerContext = React.createContext();

2 const MapViewerProvider = (props) => {
3  const [state, dispatch] = useReducer(mapViewerReducer, initialState);

4  const mapViewerData = { state, dispatch };

5  return <MapViewerContext.Provider value={mapViewerData} {...props} />;
6 };

7 const useMapViewerContext = () => {
8   return useContext(MapViewerContext);
9 };

10export { MapViewerProvider, useMapViewerContext };

我假设您的代码的用法如下。

  <MapViewerProvider>
    <Children />
  </MapViewerProvider>

  const Children = () => {
    const { state, dispatch } = useMapViewerContext()
  }

你能不能试试这个方法

  <MapViewerContext.Provider value={useReducer(..., ...)}>
     <Children />
  <MapViewerContext.Provider />
  
  const Children = () => {
    const [ state, dispatch ] = useContext(MapViewerContexxt)
  }

我知道这与您所做的非常相似。我在没有添加另一个临时变量的情况下将 {} 更改为 []。由于我的胆量是这条线,LINE 4。

 const mapViewerData = { state, dispatch };

不知何故,每次进入此渲染时,您都会获得一个新的 value 实例。