2

I'm having trouble with typing in the following Hook:

import { SetStateAction, useCallback, useEffect, useRef, useState } from 'react';

type Callback<T> = (value?: T) => void;
type DispatchWithCallback<T> = (value: T, callback?: Callback<T>) => void;

function useStateCallback<T>(initialState: T | (() => T)): [T, DispatchWithCallback<SetStateAction<T>>] {
  const [state, _setState] = useState(initialState);

  const callbackRef = useRef<Callback<T>>();
  const isFirstCallbackCall = useRef<boolean>(true);

  const setState = useCallback((setStateAction: SetStateAction<T>, callback?: Callback<T>): void => {
    callbackRef.current = callback;
    _setState(setStateAction);
  }, []);

  useEffect(() => {
    if (isFirstCallbackCall.current) {
      isFirstCallbackCall.current = false;
      return;
    }
    callbackRef.current?.(state);
  }, [state]);

  return [state, setState];
}

export default useStateCallback; 
interface InitialState {
    firstName: string
    lastName: string
} 

No Typescript errors:

const [state, setState] = useStateCallback<InitialState>({ firstName: 'Bob', lastName: 'Donovan'});
setState({ firstName: 'Claire', lastName: 'Donovan'}, (curState) => {
  console.log(curState) // => { firstName: 'Claire', lastName: 'Donovan'}
});

Typescript error in console.log(curState.firstName):

Property 'firstName' does not exist on type 'SetStateAction'. Property 'firstName' does not exist on type '(prevState: InitialState) => InitialState'.

const [state, setState] = useStateCallback<InitialState>({ firstName: 'Bob', lastName: 'Donovan'});
setState({ firstName: 'Claire', lastName: 'Donovan'}, (curState) => {
  console.log(curState.firstName) // => 'Claire'
});

What is wrong with the typing?

1
  • what's the error? Commented Dec 24, 2020 at 9:35

1 Answer 1

1

Looks like there're a few typings should be defined again from my understanding:

// The 1st argument would take `T` or `(prevState: T) => T`
type DispatchWithCallback<T> = (value: SetStateAction<T>, callback?: Callback<T>) => void;

// so then `DispatchWithCallback` should only take `T`
function useStateCallback<T>(initialState: T | (() => T)): [T, DispatchWithCallback<T>] {
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.