上下文提供者类型并在其他组件上获取值

Context Provider Type and get the value on other components

我正在使用 useContextuseReducer 来管理应用程序状态。

以下是片段:

//ReducerProvider.tsx

type ContextProviderType = {     
    state: StateInterface,
    dispatch: React.Dispatch<ActionInterface>
}

type CreateContextType = ContextProviderType | string; // i am passing to the createContext either the ContextProviderType or a string (the string is just to initialize without errors since useReducer is not declared yet)

export const ReducerContext = React.createContext<CreateContextType>('');

interface ProviderProps { 
    children: ReactNode
}

export const ReducerProvider: React.FC<ProviderProps> = ({children}): JSX.Element => {

    const [state, dispatch] = useReducer(reducer, initialState);
    
    return <>
        <ReducerContext.Provider value={{state, dispatch}}>
            {children}
        </ReducerContext.Provider>
    </>
}

//App.tsx

const App: React.FC = (): JSX.Element => {

  return <>
        <ReducerProvider>
          //components here so they can take the value of the provider component
        </ReducerProvider>
    </>;

}

只有当我尝试获取状态和分派值时才会出现错误,如下所示:

//Component.tsx

export const Component: React.FC = (): JSX.Element => {

  const { state, dispatch } = useContext(ReducerContext)
  //ERROR: Property 'state' does not exist on type 'CreateContextType'
  //ERROR: Property 'dispatch' does not exist on type 'CreateContextType'

  return (
    <div className="box">
        //...
    </div>
  )
}

但我不理解这个错误,因为我声明 CreateContextType 可以是字符串或 ContextProviderType,它是一个接收状态和调度参数的对象。

But I'm not understanding this error since I declared that CreateContextType can be either a string or a ContextProviderType,

这正是您收到错误的原因:您说它可能是一个字符串。如果它是一个字符串,那么它不会有 statedispatch 属性。由于它可能没有这些属性,TypeScript 不会让您访问它们,除非您编写代码以确保它们首先存在。

检查字符串可能如下所示:

let state;
let dispatch;
const value = useContext(ReducerContext);
if (typeof value === 'string') {
  throw new Error('Uh oh, i got a string');
} else {
  ({ state, dispatch }) = value;
}

这显然很麻烦,因此您可以将此检查提取到自定义挂钩中:

export const useReducerContext = () => {
  const value = useContext(ReducerContext);
  if (typeof value === 'string') {
    throw new Error("Don't forget to render a <ReducerProvider> higher up the tree")
  }
  return value;
}

// used like:
const { state, dispatch } = useReducerContext();

另一种选择是更改 ReducerContext 的类型,使其仅使用 ContextProviderType,而不是 string。这确实意味着您需要提供完整的默认值:

export const ReducerContext = React.createContext<ContextProviderType>({
  state: {
    // Fill out an entire fake state object here
  },
  dispatch: () => {}
});

创建一个永远不会被使用的默认值可能会很痛苦,因此您可以使用类型断言来关闭打字稿:

export const ReducerContext = React.createContext<ContextProviderType>(
  {} as ContextProviderType
)