Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to type state and dispatch for useReducer - typescript and react

I am currently seeing this error with my current setup.

Type '({ team: string | null; } | { team: string | null; } | { ...; } | { ...; } | { ...; } | Dispatch<...>)[]' is missing the following properties from type 'State': teamts(2739) index.d.ts(290, 9): The expected type comes from property 'value' which is declared here on type 'IntrinsicAttributes & ProviderProps'

My code is below, if any more detail is required let me know.

actions.ts

export const setTeam = (team: string | null) => ({
  type: 'SET_TEAM',
  team,
});

reducer.ts

export const initialState = {
  team: null,
};

type State = {
  team: string | null;
};

export const GlobalContext = React.createContext<State | null>(null);

export const reducer = (state: State, action: any) => {
  switch (action.type) {
    case actionTypes.SET_TEAM:
      const team = action.team;
      return {
        ...state,
        team,
      };

    default:
      return state;
  }
};

App.tsx

const App = () => {
  const [state, dispatch] = React.useReducer(reducer, initialState);

  return ( 
    // error with state and dispatch here
    <GlobalContext.Provider value={[state, dispatch]}>
        ...App code
    </GlobalContext.Provider>
  )
}

Team.tsx

import { GlobalContext } from './reducers';
import { setTeam } from './actions';

const Team = () => {
   const [, dispatch] = React.useContext(GlobalContext);

   return <span onClick={() => dispatch(setTeam('LFC'))}>LFC</span>
}
like image 792
peter flanagan Avatar asked Dec 20 '19 22:12

peter flanagan


People also ask

What is Dispatch in useReducer in React?

Conclusion. The useReducer() hook in React lets you separate the state management from the rendering logic of the component. const [state, dispatch] = useReducer(reducer, initialState) accepts 2 argument: the reducer function and the initial state.

Can I use useState and useReducer together?

The useReducer hook is usually recommended when the state becomes complex, with state values depending on each other or when the next state depends on the previous one. However, many times you can simply bundle your separate useState statements into one object and continue to use useState .

Is dispatch in useReducer async?

React useReducer doesn't support async actions natively. Unlike Redux, there's no middleware interface, but hooks are composable. This is a tiny library to extend useReducer's dispatch so that dispatching async actions invoke async functions.

Is useReducer better than Redux?

Can useReducer replace Redux? The useReducer hook should be used in components that have complex logic behind it. It shows as the main confusion with the Redux library, because developers tend to think that useReducer could replace the state manager library. But in fact, its use should be restricted to components.


1 Answers

If you want to pass the state and the dispatch through context you have to type it on the context, you can go with just this line but if you want type safety read further

const GlobalContext = React.createContext<[State, React.Dispatch<any>]>([
  { team: null },
  () => {},
])

You can change the <any> inside React.Dispatch to your action types if you want type safety for actions, you would also need to type the action inside the reducer

enum TeamTypes {
  SET_TEAM = 'SET_TEAM',
  REMOVE_TEAM = 'REMOVE_TEAM',
}

type State = {
  team: string | null
}

export const initialState: State = {
  team: null,
}

type SetTeamAction = {
  type: typeof TeamTypes.SET_TEAM
  team: string
}

type RemoveTeamAction = {
  type: typeof TeamTypes.REMOVE_TEAM
}

type TeamActionTypes = SetTeamAction | RemoveTeamAction

export const setTeam = (team: string): TeamActionTypes => ({
  type: TeamTypes.SET_TEAM,
  team,
})

export const GlobalContext = React.createContext<
  [State, React.Dispatch<TeamActionTypes>]
>([{ team: null }, () => {}])

export const reducer = (state: State, action: TeamActionTypes): State => {
  switch (action.type) {
    case TeamTypes.SET_TEAM:
      const { team } = action
      return {
        ...state,
        team,
      }
    default:
      return state
  }
}

const App = () => {
  const [state, dispatch] = React.useReducer(reducer, initialState)

  return (
    <GlobalContext.Provider value={[state, dispatch]}></GlobalContext.Provider>
  )
}
like image 132
Asaf Aviv Avatar answered Oct 23 '22 18:10

Asaf Aviv