DEV Community

Cover image for React hooks with TypeScript
LuisPa
LuisPa

Posted on

React hooks with TypeScript

Hi! This is a short post about how to use React Hooks with TypeScript.

Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.

Check out this page for more details about hooks. I think they’re pretty awesome. Probably game-changing! Hooks make formerly “stateless” functional components to basically everything old class components can be.

So much cleaner API, with no breaking changes.

Just quickly after their release in React 16.7., React typings in DefinitelyTyped got an update as well. Check out how you can use hooks with TypeScript!

Benefits of use TypeScript

  • Code scalability with “Interface oriented development”
  • TypeScript helps you dealing with growing teams.
  • Tooling and Community, for example, Microsoft made an awesome community effort work.
  • ES-next compliance.
  • Types have a proven ability to enhance code quality and understandability.

You can start testing this very quick using codesandbox.io or StackBlitz or locally using:

$ npx create-react-app my-app --template typescript
Enter fullscreen mode Exit fullscreen mode

In this post we will see:

  1. useState
  2. useEffect
  3. useContext

useState

useState is probably one you are going to use a lot. Instead of using this.state from class components, you can access the current state of a component instance, and initialize it, with one single function call. Our desire for strong typing is that values we initially set, get per component update, and set through events, always have the same type. With the provided typings, this works without any additional TypeScript:

// import useState next to FunctionComponent
import React, { FunctionComponent, useState } from 'react';

// our components props accept a number for the initial value
const Counter:FunctionComponent<{ initial?: number }> = ({ initial = 0 }) => {
  // since we pass a number here, clicks is going to be a number.
  // setClicks is a function that accepts either a number or a function returning
  // a number
  const [clicks, setClicks] = useState(initial);
  return <>
    <p>Clicks: {clicks}</p>
    <button onClick={() => setClicks(clicks+1)}>+</button>
    <button onClick={() => setClicks(clicks-1)}>-</button>
  </>
}
Enter fullscreen mode Exit fullscreen mode

And that’s it. Your code works with out any extra type annotations, but still typechecks.

useEffect

useEffect is here for all side effects. Adding event listeners, changing things in the document, fetching data. Everything you would use component lifecycle methods for (componentDidUpdate, componentDidMount, componentWillUnmount) The method signature is pretty straightforward. It accepts two parameters:

  • A function that is called without any parameters. This is the side-effect you want to call.
  • An array of values of type any. This parameter is optional. If you don’t provide it, the function provided is called every time the component update. If you do, React will check if those values did change, and triggers the function only if there’s a difference.
// Standard use case.
const [name, setName] = useState('Stefan');
useEffect(() => {
  document.title = `Hello ${name}`;
}, [name])
Enter fullscreen mode Exit fullscreen mode

You don’t need to provide any extra typings. TypeScript will check that the method signature of the function you provide is correct. This function also has a return value (for cleanups). And TypeScript will check that you provide a correct function as well:

useEffect(() => {
  const handler = () => {
    document.title = window.width;
  }
  window.addEventListener('resize', handler);

  // won't compile
  return true;

  // compiles
  return () => {
    window.removeEventListener('resize', handler);
  }
})
Enter fullscreen mode Exit fullscreen mode

This also goes for useLayoutEffect and useMutationEffect.

useContext

useContext allows you to access context properties from anywhere in your components. Much like the Context.Consumer does in class components. Type inference works brilliantly here, you don’t need to use any TypeScript specific language features to get everything done:

import React, { useContext } from 'react';

// our context sets a property of type string
export const LanguageContext = React.createContext({ lang: 'en' });

const Display = () => {
  // lang will be of type string
  const { lang } = useContext(LanguageContext);
  return <>
    <p>Your selected language: {lang}</p>
  </>
}
Enter fullscreen mode Exit fullscreen mode

And that's it. We just learned how to use 3 of the most used hooks from React Hooks with TypeScript.

Happy coding!

Discussion (1)

Collapse
seanmclem profile image
Seanmclem

I feel like there's some typescript missing