Passing useState as props in typescript

27,064

Solution 1

The type that would match the function returned from invoking useState would be:

setMyVar: (value: boolean | ((prevVar: boolean) => boolean)) => void;

If we look at the type definition file from DefinitelyTyped [1], we can see that the second type in the return type is a dispatch:

function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];

Thus the generic type provided is passed through to SetStateAction<S>, which is defined as:

type SetStateAction<S> = S | ((prevState: S) => S);

So essentially, an interface for your component would be the following:

interface IProps {
  myVar: boolean;
  setMyVar?: (value: boolean | (prevVar: boolean) => boolean) => void;
}

As @Retsam said, it's best to use React's exported types:

import { Dispatch, SetStateAction } from "react";

interface IProps {
  myVar: boolean;
  setMyVar?: Dispatch<SetStateAction<boolean>>;
}

References: [1] https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts#L845

Solution 2

Dispatch & SetStateAction types

As @Retsam mentioned, you can also import and use the types Dispatch and SetStateAction from React:

import React, { Dispatch, SetStateAction } from 'react';

const MyChildComponent1 = (
  myVar: boolean,
  setMyVar: Dispatch<SetStateAction<boolean>>
) => {...};

Bonus

When I find myself frequently using this, I create a type alias to help with readability

import React, { Dispatch, SetStateAction } from 'react';

type Dispatcher<S> = Dispatch<SetStateAction<S>>;

const MyChildComponent1 = (
  myVar: boolean,
  setMyVar: Dispatcher<boolean>,
) => {...};

hope this helps.

Solution 3

Adding onto @fiz's comment, his code of block was slightly not working for me:

import React, { Dispatch, SetStateAction } from 'react';

const MyChildComponent1 = (
  myVar: boolean,
  setMyVar: Dispatch<SetStateAction<<boolean>>
) => {...};

I had to set setMyVar: Dispatch<SetStateAction<boolean>> (there was one too many brackets)

Solution 4

It can also be done like this using Interface and React components.

MainComponent.tsx

Value assignment of the useState component defined in Main is done in the child component. When this field is triggered, the code in useEffect will run.

import React, { useEffect } from 'react';
import Login from './views/Login/index';

const App: React.FunctionComponent = () => {
    const [isAuthenticated, setAuthenticatedStatus] = React.useState(false);

    useEffect(() => {
        if (isAuthenticated)
            window.location.href = "<redirect url>";
    }, [isAuthenticated]);

    return (<Login setLoginStatus={setAuthenticatedStatus} />)
};

export default App;

ChildComponent.tsx

import { Dispatch, SetStateAction, FunctionComponent } from 'react';
import authService from '../../apiServices/authService';

interface IProps {
   setLoginStatus: Dispatch<SetStateAction<boolean>>;
}

const Login: FunctionComponent<IProps> = (props: IProps) => {

   const login = async (username: string, password: string) => {
      const response = await authService.authenticateUser(username, password);

      if (response && response.statusCode == 200 && response.result.accessToken) {
         props.setLoginStatus(true);
      }
      else {
         // ...
      }
   };


   return (
      <>
         ...
      </>
   );
};

export default Login;
Share:
27,064
Arnab Datta
Author by

Arnab Datta

Updated on January 22, 2022

Comments

  • Arnab Datta
    Arnab Datta over 2 years

    Say I have a parent component with two child components:

    const Parent = () => {
       const [myVar, setmyVar] = useState(false)
    
       return (
         <>
           <MyChildComponent1 myVar={myVar} setMyVar={setMyVar} \> 
           <MyChildComponent2 myVar={myVar} \>
         </>
       )
    }
    

    Now how would I go about setting the type correctly in MyChildComponent2?

    This is what I've come up with so far:

    const MyChildComponent1 = (
      {myVar, setMyVar}: 
      {myVar: boolean, setMyVar: (value: boolean) => void}) = (...)
    

    Is the type for setMyvar correct? Or should it be something else?