4

而不是任何,我想在下面的代码中使用正确的 TS 类型。我是反应 TS 的新手,请帮助...

如何为以下上下文 API 代码的 useReducer useContext 设置 typescript 类型:

import React, {createContext, Dispatch} from 'react';
import {firebaseUser} from '../@types/User';

interface Actions {
  SET_IMAGENAME: string;
  SET_USER: string;
}

export const Actions: Actions = {
  SET_IMAGENAME: 'SET_IMAGENAME',
  SET_USER: 'SET_USER',
};

function action(type: string) {
  return {type};
}

function actionPayload(type: string, payload: any) { //here
  return {type, payload};
}

export const Dispatches = {
  setImageName: action,
  setUser: actionPayload,
};

interface State {
  imgName: string;
  user: firebaseUser;
}

const initialState = {
  imgName: '',
  user: {} as firebaseUser,
};

function reducer(state = initialState, action: {type: string; payload: any}) { //here
  switch (action.type) {
    case Actions.SET_IMAGENAME:
      return {...state, imgName: 'sample image'};
    case Actions.SET_USER:
      return {...state, user: action.payload};
    default:
      return state;
  }
}

export const Store = createContext<{
  state: State;
  dispatch: Dispatch<any>; //here
}>({
  state: initialState,
  dispatch: () => null,
});

export function StoreProvider({children}: JSX.ElementChildrenAttribute): JSX.Element {
  const [state, dispatch] = React.useReducer(reducer, initialState);
  return <Store.Provider value={{state, dispatch}}>{children}</Store.Provider>;
}

任何人都可以帮助我吗?将不胜感激?

谢谢你

4

3 回答 3

3

我希望这个解决方案能给你一个好主意。

https://gist.github.com/sw-yx/f18fe6dd4c43fddb3a4971e80114a052

https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/context/#extended-example

export function createCtx<StateType, ActionType>(
  reducer: React.Reducer<StateType, ActionType>,
  initialState: StateType,
) {
  const defaultDispatch: React.Dispatch<ActionType> = () => initialState // we never actually use this
  const ctx = React.createContext({
    state: initialState,
    dispatch: defaultDispatch, // just to mock out the dispatch type and make it not optioanl
  })
  function Provider(props: React.PropsWithChildren<{}>) {
    const [state, dispatch] = React.useReducer<React.Reducer<StateType, ActionType>>(reducer, initialState)
    return <ctx.Provider value={{ state, dispatch }} {...props} />
  }
  return [ctx, Provider] as const
}
// usage
const initialState = { count: 0 }
type AppState = typeof initialState
type Action =
  | { type: 'increment' }
  | { type: 'add'; payload: number }
  | { type: 'minus'; payload: number }
  | { type: 'decrement' }

function reducer(state: AppState, action: Action): AppState {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 }
    case 'decrement':
      return { count: state.count - 1 }
    case 'add':
      return { count: state.count + action.payload }
    case 'minus':
      return { count: state.count - action.payload }
    default:
      throw new Error()
  }
}
const [ctx, CountProvider] = createCtx(reducer, initialState)
export const CountContext = ctx

// top level example usage
export function App() {
  return (
    <CountProvider>
      <Counter />
    </CountProvider>
  )
}

// example usage inside a component
function Counter() {
  const { state, dispatch } = React.useContext(CountContext)
  return (
    <div>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'add', payload: 5 })}>+5</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <button onClick={() => dispatch({ type: 'minus', payload: 5 })}>+5</button>
    </div>
  )
}

于 2021-05-20T02:43:04.847 回答
1

这实际上取决于有效负载可以包含什么,它可以包含图像字符串或您可以设置的 FirebaseUser 实例payload: string | FirebaseUser

import React, {createContext, Dispatch} from 'react';
import {firebaseUser} from '../@types/User';

interface Actions {
  SET_IMAGENAME: string;
  SET_USER: string;
}

export const Actions: Actions = {
  SET_IMAGENAME: 'SET_IMAGENAME',
  SET_USER: 'SET_USER',
};

function action(type: string) {
  return {type};
}

// If payload can contain string of image or FirebaseUser instance
// it will be string | FirebaseUser
// if payload will only contain FirebaseUser instance you just need payload: FirebaseUser
export type ActionType = {
 type: string,
 payload: string | FirebaseUser
}

// If payload can contain string of image or FirebaseUser instance
// it will be string | FirebaseUser
// if payload will only contain FirebaseUser instance you just need payload: FirebaseUser
function actionPayload(type: string, payload: string | FirebaseUser ) { //here
  return {type, payload};
}

export const Dispatches = {
  setImageName: action,
  setUser: actionPayload,
};

interface State {
  imgName: string;
  user: FirebaseUser;
}

const initialState = {
  imgName: '',
  user: {} as firebaseUser,
};

// set Action type here
function reducer(state = initialState, action: ActionType) {
  switch (action.type) {
    case Actions.SET_IMAGENAME:
      // need to cast type herer
      return {...state, imgName: action.payload as string};
    case Actions.SET_USER:
      // need to cast type herer
      return {...state, user: action.payload as firebaseUser};
    default:
      return state;
  }
}

export const Store = createContext<{
  state: State;
  dispatch: Dispatch<ActionType>; //action type here
}>({
  state: initialState,
  dispatch: () => null,
});

export function StoreProvider({children}: JSX.ElementChildrenAttribute): JSX.Element {
  const [state, dispatch] = React.useReducer(reducer, initialState);
  return <Store.Provider value={{state, dispatch}}>{children}</Store.Provider>;
}
于 2020-07-19T08:56:45.733 回答
0

这也是我的解决方案,通过添加类型文件来更清洁您的文件结构,我希望您发现它有用: myCodesandboxRepo

于 2020-07-25T13:47:48.433 回答