spot_img
HomeEducationWrapping React.useState with TypeScript Acquire US

Wrapping React.useState with TypeScript Acquire US

I made a useDarkMode hook that appears like this:

kind DarkModeState = 'darkish' | 'mild'
kind SetDarkModeState = React.Dispatch<React.SetStateAction<DarkModeState>>

perform useDarkMode() 
  const preferDarkQuery = '(prefers-color-scheme: darkish)'
  const [mode, setMode] = React.useState<DarkModeState>(() => 
    const lsVal = window.localStorage.getItem('colorMode')
    if (lsVal) 
      return lsVal === 'darkish' ? 'darkish' : 'mild'
     else 
      return window.matchMedia(preferDarkQuery).matches ? 'darkish' : 'mild'
    
  )

  React.useEffect(() => 
    const mediaQuery = window.matchMedia(preferDarkQuery)
    const handleChange = () => 
      setMode(mediaQuery.matches ? 'darkish' : 'mild')
    
    mediaQuery.addEventListener('change', handleChange)
    return () => mediaQuery.removeEventListener('change', handleChange)
  , [])

  React.useEffect(() => 
    window.localStorage.setItem('colorMode', mode)
  , [mode])

  // we're doing it this manner as an alternative of as an impact so we solely
  // set the localStorage worth in the event that they explicitly change the default
  return [mode, setMode] as const

Then it’s used like this:

perform App() 
  const [mode, setMode] = useDarkMode()
  return (
    <>
      /* ... */
      <Dwelling mode=mode setMode=setMode />
      /* ... */
      <Web page mode=mode setMode=setMode />
      /* ... */
    </>
  )


perform Dwelling(
  mode,
  setMode,
: 
  mode: DarkModeState
  setMode: SetDarkModeState
) 
  return (
    <>
      /* ... */
      <Navigation mode=mode setMode=setMode />
      /* ... */
    </>
  )


perform Web page(
  mode,
  setMode,
: 
  mode: DarkModeState
  setMode: SetDarkModeState
) 
  return (
    <>
      /* ... */
      <Navigation mode=mode setMode=setMode />
      /* ... */
    </>
  )


perform Navigation(
  mode,
  setMode,
: 
  mode: DarkModeState
  setMode: SetDarkModeState
) 
  return (
    <>
      /* ... */
      <button onClick=() => setMode(mode === 'mild' ? 'darkish' : 'mild')>
        mode === 'mild' ? <RiMoonClearLine /> : <RiSunLine />
      </button>
      /* ... */
    </>
  )

This works nice, and powers the “darkish mode” assist for all of the
Epic React workshop apps (for instance
React Fundamentals).

I need to name out a couple of issues concerning the hook itself that made issues work effectively
from a TypeScript perspective. First, let’s filter out all the additional stuff and
simply have a look at the necessary bits. We’ll even filter out the TypeScript and add it
iteratively:

perform useDarkMode() 
  const [mode, setMode] = React.useState(() => 
    // ...
    return 'mild'
  )

  // ...

  return [mode, setMode]


perform App() 
  const [mode, setMode] = useDarkMode()
  return (
    <button onClick=() => setMode(mode === 'mild' ? 'darkish' : 'mild')>
      Toggle from mode
    </button>
  )

From the get-go, we have got an error when calling setMode:

This expression just isn't callable.
  Not all constituents of kind 'string | React.Dispatch<SetStateAction<string>>' are callable.
    Sort 'string' has no name signatures.(2349)

You possibly can learn every addition of indentation as “as a result of”, so let’s learn that
once more:

This expression just isn’t callable. As a result of not all constituents of kind
'string | React.Dispatch<SetStateAction<string>>' are callable. As a result of
kind 'string' has no name signatures.(2349)

The “expression” it is referring to is the decision to setMode, so it is saying that
setMode is not callable as a result of it may be both
React.Dispatch<SetStateAction<string>> (which is a callable perform) or
string (which isn’t callable).

For us studying the code we all know that setMode is a callable perform, so the
query is: why is the setMode kind each a perform and a string?

Let me rewrite one thing and we’ll see if the explanation jumps out at you:

const array = useDarkMode()
const mode = array[0]
const setMode = array[1]

The array on this case has the next kind:

Array<string | React.Dispatch<React.SetStateAction<string>>>

So the array that is being returned from useDarkMode is an Array with
components which are both a string or a React.Dispatch kind. So far as
TypeScript is anxious, it has no concept that the primary aspect of the array is
the string and the second aspect is the perform. All it is aware of for certain is that
the array has components of these two varieties. So after we pull any values out of
this array, these values should be one of many two varieties.

However React’s useState hook manages to make sure after we extract values out of it.
Let’s take a fast have a look at their kind definition for useState:

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

Ah, in order that they have a return kind that’s an array with specific varieties. So somewhat
than an array of components that may be one in every of two varieties, it is explicitly an array
with two components the place the primary is the kind of state and the second is a
Dispatch SetStateAction for that kind of state.

So we have to inform TypeScript that we intend to make sure our array values do not
ever change. There are a couple of methods to do that, we might set the return kind for
our perform:

perform useDarkMode(): [string, React.Dispatch<React.SetStateAction<string>>] 
  // ...
  return [mode, setMode]

Or we might make a particular kind for a variable:

perform useDarkMode() 
  // ...
  const returnValue: [string, React.Dispatch<React.SetStateAction<string>>] = [
    mode,
    setMode,
  ]
  return returnValue

Or, even higher, TypeScript has this functionality built-in. As a result of TypeScript
already is aware of the kinds in our array, so we are able to simply inform TypeScript: “the kind
for this worth is fixed” so we are able to solid our price as a const:

perform useDarkMode() 
  // ...
  return [mode, setMode] as const

And that makes all the pieces joyful with out having to spend a ton of time typing out
our varieties

And we are able to take it a step additional as a result of with our Darkish Mode performance, the
string may be both darkish or mild so we are able to do higher than TypeScript’s
inference and cross the doable values explicitly:

perform useDarkMode() 
  const [mode, setMode] = React.useState<'darkish' 

It will assist us after we name setMode to make sure we not solely name it with a
string, however the proper kind of string. I additionally created kind aliases for this and
the dispatch perform to make the prop varieties simpler as I cross these values
round my app.

Hope that was attention-grabbing and useful to you! Get pleasure from

#Wrapping #React.useState #TypeScript

RELATED ARTICLES
Continue to the category

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -spot_img

Most Popular

Recent Comments