Docs / ReasonReact / HooksOverview

Hooks Overview

Hooks are an essential mechanism to introduce and manage state and effects in React components.

What is a Hook?

In the previous chapters we learned how React components are just a simple function representing UI based on specific prop values. For an application to be useful we still need a way to manipulate those props interactively either via user input or via requests loading in data from a server.

That's were Hooks come in. A Hook is a function that allows us to introduce component state and trigger side-effects for different tasks, such as HTTP requests, direct HTML DOM access, querying window sizes, etc.

In other words: It allows us to "hook into" React features.

Example: The useState Hook

Just for a quick look, here is an example of a Counter component that allows a user to click a button and increment an count value that will immediately be rendered on each button click:

ReScriptJS Output
 
// Counter.re
@react.component
let make = () => {
  let (count, setCount) = React.useState(_ => 0);

  let onClick = (_evt) => {
    setCount(prev => prev + 1)
  };

  let msg = "You clicked" ++ Belt.Int.toString(count) ++  "times"

  <div>
    <p>{React.string(msg)}</p>
    <button onClick> {React.string("Click me")} </button>
  </div>
}

Here we are using the React.useState Hook. We call it inside a component function to add some local state to it. React will preserve this state between re-renders. React.useState returns a tuple: the current state value (count) and a function that lets you update it (setCount). You can call this function from an event handler or pass it down to other components to call the function.

The only argument to React.useState is a function that returns the initial state (_ => 0). In the example above, it is 0 because our counter starts from zero. Note that your state can be any type you want and ReScript will make sure to infer the types for you (only make sure to return an initial state that matches your type). The initial state argument is only used during the first render.

This was just a quick example on our first hook usage. We will go into more detail in a dedicated useState section.

Available Hooks

Note: All hooks are part of the React module (e.g. React.useState).

Basic Hooks:

  • useState: Adds local state to your component

  • useEffect: Runs side-effectual code within your component

  • useContext: Gives your component to a React Context value

Additional Hooks:

  • useReducer: An alternative to useState. Uses the state / action / reduce pattern.

  • useCallback: Returns a memoized callback

  • useMemo: Returns a memoized value

  • useRef: Returns a mutable React-Ref value

  • useImperativeHandle: Customizes the instance value that is exposed to parent components when using ref

  • useLayoutEffect: Identical to useEffect, but it fires synchronously after all DOM mutations.

Rules of Hooks

Hooks are just simple functions, but you need to follow two rules when using them. ReScript doesn't enforce those rules within the compiler, so if you really want to enforce correct hooks conventions, you can use an eslint-plugin to check your compiled JS output.

Rule 1) Only Call Hooks at the Top Level

Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function. By following this rule, you ensure that Hooks are called in the same order each time a component renders. That’s what allows React to correctly preserve the state of Hooks between multiple useState and useEffect calls. (If you’re curious, you can check out the in depth explanation in the ReactJS Hooks docs)

Rule 2) Only Call Hooks from React Functions

Don't call Hooks from regular functions. Instead, you can:

  • ✅ Call Hooks from React function components.

  • ✅ Call Hooks from custom Hooks (we’ll learn about them in our custom hooks section).

By following this rule, you ensure that all stateful logic in a component is clearly visible from its source code.