Skip to content

React Hooks

Hooks are a new addition in React 16.8. They allow you use state, life cycle methods and other React features without writing a class component. If we are using hooks we can have only a functional component in the entire application. For more detail explanation you check React documentation.

Different hooks have been introduced to React: Basic hooks and additional hooks

Basic Hooks

The basic hooks are:

  • useState
  • useEffect
  • useContext

State Hook

Using hooks we can access state.

To use hooks, first we should import the useState hooks from react. The useState is a function which takes one argument (the initial value) and returns an array of the current state and a function that lets you update it.

State with Data Types

If you use useState with primitive data types, there is no problem by using operators to set the state with the current value, like in our example above.

But if you use non-primitive data types you can't just change the value and set it.

Note

The reason is that React compares object by strict equality ===.

Strict equality works for strings, numbers and booleans.

For example for strings:

const one = 'one'
const anotherOne = 'one'
const two = 'two'

one === anotherOne
one !== two

For example for numbers:

const one = 1
const anotherOne = 1
const two = 2

one === anotherOne
one !== two

For example for booleans:

const one = true
const anotherOne = true
const two = false

one === anotherOne
one !== two

But it doesn't work for arrays, objects and functions (functions and arrays are objects under the hood), because JavaScript compares the memory address of the variable containing arrays, objects and functions.

For example for arrays:

const one = ['one']
const anotherOne = ['one']
const two = ['two']

one !== anotherOne
one !== two

For example for objects:

const one = { one: 'value' }
const anotherOne = { one: 'value' }
const two = { two: 'value' }

one !== anotherOne
one !== two

For example for functions:

const one = () => 'one'
const anotherOne = () => 'one'
const two = () => 'two'

one !== anotherOne
one !== two

You have to create a "new" object or copy the old one and set it.

Here is a bad example, which won't work:

Here is the fixed example:

The same happens to arrays:

Here is the fixed examle:

When a state changes, React will check if the state is used somewhere and update the element.

Effect Hook

Effects happen, when a state or a prop changes. To watch for changes and do a sideeffect (not computing a value or not preparing a callback) you can use useEffect.

Context

useContext will be discussed later in the Context chapter.

Additional Hooks

Additional Hooks can be used to express specific statements or to speed up your code.

  • useReducer
  • useCallback
  • useMemo
  • useRef
  • useImperativeHandle
  • useLayoutEffect
  • useDebugValue
  • useDeferredValue
  • useTransition
  • useId

Reducer

Reducer are handy if you have multiple actions for the same state. For example, if you work on the same state with different functions, then a reducer can be used to define the actions on a state in one place:

Callbacks

Callbacks are important to pass a behaviour into a component from an outer scope. Let's check this example first:

You see that an update of our states results in an update for our buttons. Let's improve the code by using useCallback to listen for specific changes:

Now the buttons does not update if the other button gets clicked. This can lead to massive performance improvements.

Memo

If you have some operations, which take a lot of computing time, useMemo. Actually I would say, use always useMemo if you calculate something depending on a state or prop.

Here is a bad example:

Here we use useMemo, additionally we define the dependency:

Ref

What if you need to access to the actual html element in the dom, to call a function on the element? You can useRef for that:

Imperative Handle

Layout Effect

useLayoutEffect works the same way useEffect works. The signature is identical to useEffect, but it fires synchronously after all DOM mutations. Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint.

Debug Value

useDebugValue shows a value in React DevTools.

function useFriendStatus(friendID) {
    const [isOnline, setIsOnline] = useState(null)

    // Show a label in DevTools next to this Hook  // e.g. "FriendStatus: Online"
    useDebugValue(isOnline ? 'Online' : 'Offline')
    return isOnline
}

Deferred Value

useDeferredValue receives a state variable and returns a new state variable, which will wait until all rendering finishes for the given state variable and then rerenders the deferred value.

function Typeahead() {
    const query = useSearchQuery('')
    const deferredQuery = useDeferredValue(query)

    // Memoizing tells React to only re-render when deferredQuery changes,
    // not when query changes.
    const suggestions = useMemo(
        () => <SearchSuggestions query={deferredQuery} />,
        [deferredQuery],
    )

    return (
        <>
            <SearchInput query={query} />
            <Suspense fallback="Loading results...">{suggestions}</Suspense>
        </>
    )
}

Transition

useTransition can handle loading states.

ID

useId is a hook for generating unique IDs that are stable across the server and client, while avoiding hydration mismatches.

Warning

useId is not for generating keys in a list. Keys should be generated from your data.

function Checkbox() {
    const id = useId()
    return (
        <>
            <label htmlFor={id}>Do you like React?</label>
            <input id={id} type="checkbox" name="react" />
        </>
    )
}