useEffect cleanup function Typescript error: not assignable to parameter of type 'EffectCallback'

Viewed 1263

I have this block of code that should do the following:

  • Dispatch a thunk do load data after 1st render
  • Dispatch an action to reset state as the cleanup function for that effect

Since dispatch and slug won't change during this component lifecycle, the cleanup will basically run after the component unmounts.

const dispatch = useDispatch();
const slug = props.match.params.slug || "";

useEffect(() => {
  dispatch(THUNKS.LOAD_DATA(slug));
  return () => { dispatch(ACTIONS.RESET_STATE()); };
},[dispatch,slug]);

Everything is working as expected. But I would like to write the cleanup function as:

return () => dispatch(ACTIONS.RESET_STATE());

I know that arrow functions add an implicit return statement if when I write it like that. So basically I'm returning the dispatch() call.

And because of this, Typescript complains with the following error msg:

Argument of type '() => () => { payload: undefined; type: string; }' is not assignable to parameter of type 'EffectCallback'.

What I think is happening: EffectCallback is probably expecting void as the return, and suddenly I'm returning a dispatch() call.

Can I custom type the EffectCallback somehow to accept this return call and get rid of this error?

1 Answers

The problem is dispatch actually returns the action object so it's not a void function. You want to just ignore the returned value.

I have a solution that might seem silly unless this is a pattern that you use a lot, because all we are doing is moving the curly braces to another place. But potentially it's one place instead of many.

Instead of messing with typescript definitions, we can override useDispatch with our own custom version that actually doesn't return anything.

import { useDispatch as useRegularDispatch } from "react-redux";
import { Action, AnyAction } from "redux";

export interface VoidDispatch<A extends Action = AnyAction> {
  <T extends A>(action: T): void;
}

export const useDispatch = <A extends Action = AnyAction>(): VoidDispatch<A> => {
  const regularDispatch = useRegularDispatch();

  return (action: A) => { regularDispatch(action); };
};

Then you can use dispatch in a useEffect cleanup without any issues.

Related