65

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?

Arnab Datta
  • 5,356
  • 10
  • 41
  • 67
  • 1
    Clearer answer https://stackoverflow.com/questions/59261671/how-do-i-change-props-from-a-child-component-using-hooks/ – tim Oct 19 '20 at 14:40

7 Answers7

87

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

ljbc1994
  • 2,044
  • 11
  • 13
27

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.

cisco
  • 802
  • 9
  • 12
5

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)

Alex Pham
  • 51
  • 1
  • 2
  • 2
    Thanks for correcting the syntax error. Btw, you can edit the answer next time for small errors like this or other marginal improvements, instead of creating an entire other post. Welcome to the community! – cisco Sep 17 '20 at 04:58
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;
alignnc
  • 91
  • 1
  • 4
2

I found the other answers slightly confusing, so here's what worked for me:

Instantiate useState() in parent.ts:

const [someState, setSomeState] = useState<stateType>(initialState)

Pass in setSomeState() to child.ts:

<Child
    setSomeState={setSomeState}
/>

Make sure to only pass the name without the ()

In child.ts set Props like so:

import Dispatch from 'react'

interface Props {setSomeState: Dispatch<stateType>}
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Dhara
  • 31
  • 3
0

MyChildComponent1.tsx
set type Props like this:

import React from 'react'

type Props = {
myVar:boolean;
setmyVar: React.Dispatch<React.SetStateAction<boolean>>;
}
const MyChildComponent = ({myVar, setmyVar}: Props) => {

... YOUR CODE ...


}
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Sep 06 '22 at 07:26
0

Expanding on my answer on another question. which obtains the useState return type, the child component prop types can just be declared as the first and second items of this ReturnType

type UseStateHook = ReturnType<typeof useState<boolean>>

const MyChildComponent1 = (
    myVar: UseStateHook[0]
    setMyVar: UseStateHook[1]
) => {...};
andyb
  • 43,435
  • 12
  • 121
  • 150