React Hooks
Tags: react, webdev, javascript
Published: 2024-05-02 07:33:48


## useState
The `useState` hook is used to add state to a function component. This hook must be called from inside a component or another hook.

```javascript
import { useState } from 'react';

const [state, setState] = useState(initialState);
```

The `useState` hook returns an array with two values. The first value is the current state and the second value is a function that you can use to update the state. The `initialState` is the initial value of the state.

This hook should be used when you want to add state to a function component. For example if you want to add a counter to a component you can use the `useState` hook.

```javascript
function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>{count}</h1>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <button onClick={() => setCount(count - 1)}>Decrement</button>
    </div>
  );
}
```

If you want to add a form to a component you can use the `useState` hook to store the form values.

```javascript
function Form() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');

  return (
    <form>
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
      />
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
      />
    </form>
  );
}
```

If you want to add a toggle to a component you can use the `useState` hook to store the toggle value.

```javascript
function Toggle() {
  const [isToggled, setIsToggled] = useState(false);

  return (
    <div>
      <h1>{isToggled ? 'ON' : 'OFF'}</h1>
      <button onClick={() => setIsToggled(!isToggled)}>Toggle</button>
    </div>
  );
}
```

The `useState` hook can only be used on client side components. If you want to add state to a server side component you can use the `useState` hook in a custom hook and then use the custom hook in the server side component.

```javascript
function useCounter() {
  const [count, setCount] = useState(0);
  return [count, setCount];
}
```


## useEffect
The `useEffect` hook is used to watch for external changes and run side effects. This hook must be called from inside a component or another hook.

```javascript
import { useEffect } from 'react';

useEffect(() => {
  // Run side effect
  return () => {
    // Clean up
  };
}, [dependencies]);
```

The first parameter is the side effect that you want to run and the second parameter is an array of dependencies. If any of the dependencies change the side effect will be re-run.

This hook should be used when you want to run side effects in function components. For example if you want to fetch data from an API when the component mounts you can use the `useEffect` hook.

```javascript
function Profile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetchUser(userId).then((data) => setUser(data));
  }, [userId]);

  return <h1>{user.name}</h1>;
}
```

An external change is anything that is outside of *react* this can be another system or a browser API. For example if you want to display a message when users are offline you can use a browser API to check if the user is online.

```javascript
function Status() {
  const [isOnline, setIsOnline] = useState(null);

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  }, [friendID]);

  return isOnline;
}
```

If you want to set a function on a timer you can use the `useEffect` hook to set the timer when the component mounts and clear the timer when the component unmounts.

```javascript
function Timer() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const id = setInterval(() => {
      setCount(count + 1);
    }, 1000);
    return () => clearInterval(id);
  }, [count]);

  return <h1>{count}</h1>;
}
```

If you want to response to a window mouse event you would use `useEffect` to add the event listener when the component mounts and remove the event listener when the component unmounts.

```javascript
function Mouse() {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  useEffect(() => {
    function handleMouseMove(event) {
      setPosition({
        x: event.clientX,
        y: event.clientY,
      });
    }

    window.addEventListener('mousemove', handleMouseMove);
    return () => window.removeEventListener('mousemove', handleMouseMove);
  }, []);

  return <h1>{position.x}, {position.y}</h1>;
}
```

## useLayoutEffect
The `useLayoutEffect` hook is similar to the `useEffect` hook but it runs synchronously after the DOM has been updated. This hook must be called from inside a component or another hook.

```javascript
import { useLayoutEffect } from 'react';

useLayoutEffect(() => {
  // Run side effect
  return () => {
    // Clean up
  };
}, [dependencies]);
```

The first parameter is the side effect that you want to run and the second parameter is an array of dependencies. If any of the dependencies change the side effect will be re-run.

This hook should be used when you want to run side effects that need to be run synchronously after the DOM has been updated. For example if you want to measure the size of an element after it has been rendered you can use the `useLayoutEffect` hook.

```javascript
function Measure() {
  const [height, setHeight] = useState(0);

  const measuredRef = useLayoutEffect((node) => {
    if (node !== null) {
      setHeight(node.getBoundingClientRect().height);
    }
  }, []);

  return (
    <div ref={measuredRef}>
      <h1>Height: {height}</h1>
    </div>
  );
}
```

## useContext
This `useContext` hook is used to read a context value from the nearest provider for the context. The `useContext` hook accepts a context object and returns the current context value for that context.

```javascript
import { useContext } from 'react';

const value = useContext(MyContext);
```

The `MyContext` is the the provider that you want to read the value from. The `useContext` hook will return the current context value for that context. If the context value changes then the component will re-render.
To create a provider you can define this on a component.

```javascript
const MyContext = React.createContext(defaultValue);
```

Then you can use the `MyContext.Provider` to provide the value to the children components.

```javascript
<MyContext.Provider value={/* some value */}>
  <Child />
</MyContext.Provider>
```

The `useContext` hook is useful when you want to avoid passing props through intermediate components. For example if you have a theme that you want to pass to multiple components you can use the `useContext` hook to avoid passing the theme prop to all the components.

```javascript
const ThemeContext = React.createContext('light');

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Toolbar />
      <Footer />
    </ThemeContext.Provider>
  );
}

function Toolbar() {
  const theme = useContext(ThemeContext);
  return <div>Theme: {theme}</div>;
}

function Footer() {
  const theme = useContext(ThemeContext);
  return <div>Theme: {theme}</div>;
}
```

This can be used at many levels of the component tree to avoid passing props through intermediate components all the way down the tree. Any child component of Toolbar or Footer will be able to access the ThemeContext value.

## useReducer
The `useReducer` hook is used to manage complex state logic in a component. This hook must be called from inside a component or another hook.

```javascript
import { useReducer } from 'react';

const initialState = { count: 0 };

const reducer = (state, action) => {
    switch (action.type) {
        case 'INCREMENT':
            return { count: state.count + 1 };
        case 'DECREMENT':
            return { count: state.count - 1 };
        default:
            return state;
    }
};

const [state, dispatch] = useReducer(reducer, initialState);
```

This should be used when you have complex state logic that involves multiple sub-values or when the next state depends on the previous state. For example if you have a counter that you want to increment or decrement you can use the `useReducer` hook like the example above.

```javascript
function Counter() {
  const initialState = { count: 0 };

  const reducer = (state, action) => {
    switch (action.type) {
      case 'INCREMENT':
        return { count: state.count + 1 };
      case 'DECREMENT':
        return { count: state.count - 1 };
      default:
        return state;
    }
  };

  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <h1>{state.count}</h1>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement</button>
    </div>
  );
}
```

The reason why you would use this instead of `useState` directly is when the state logic is more complex. For example if you have a form that has multiple fields and you want to update the state based on the field that is being updated you can use the `useReducer` hook.

```javascript
function Form() {
  const initialState = { name: '', email: '' };

  const reducer = (state, action) => {
    switch (action.type) {
      case 'CHANGE_NAME':
        return { ...state, name: action.payload };
      case 'CHANGE_EMAIL':
        return { ...state, email: action.payload };
      default:
        return state;
    }
  };

  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <form>
      <input
        type="text"
        value={state.name}
        onChange={(e) => dispatch({ type: 'CHANGE_NAME', payload: e.target.value })}
      />
      <input
        type="email"
        value={state.email}
        onChange={(e) => dispatch({ type: 'CHANGE_EMAIL', payload: e.target.value })}
      />
    </form>
  );
}
```

## useCallback
`useCallback` will let you cache a function so that it doesn't change between renders. This is useful when you want to pass a function to a child component that relies on the function not changing.

```javascript
const memoizedCallback = useCallback(
    () => {doSomething(a, b);},
    [a, b]
);
```

The first parameter is the function you want to memoize and the second parameter is an array of dependencies. If any of the dependencies change the function will be re-created.

This hook should be used when you want to optimize performance by avoiding unnecessary re-renders. For example if you have a component that takes a function as a prop and you don't want it to re-render when the parent component re-renders. This is when you will use the `useCallback` hook.

In this example

```javascript
function ProductPage({ productId, referrer, theme }) {
  // ...
  return (
    <div className={theme}>
      <ShippingForm onSubmit={handleSubmit} />
    </div>
  );
```

The ShippingForm takes a parameter of `handleSubmit` but it is nested inside a div which has a theme parameter, when theme is changed React will re-render the child components and therefore the `handleSubmit` function will be re-created. To avoid this you can use the `useCallback` hook.

```javascript
function ProductPage({ productId, referrer, theme }) {
  const handleSubmit = useCallback(() => {
    // ...
  }, [productId, referrer]);

  return (
    <div className={theme}>
      <ShippingForm onSubmit={handleSubmit} />
    </div>
  );
}
```

Now the `handleSubmit` function will only be re-created when `productId` or `referrer` change.

The `useCallback` hook is similar to the `useMemo` hook but it is used for functions instead of values. `useMemo` will cache the value of the function and `useCallback` will cache the function itself, avoiding the function to be rebuilt. Therefore you can achieve the samething by doing the following.

```javascript
function useCallback(fn, deps) {
  return useMemo(() => fn, deps);
}
```

If you are writing custom hooks then you can use the `useCallback` hook to memoize the function that you are returning.

```javascript
function useHandleSubmit(productId, referrer) {
  return useCallback(() => {
    // ...
  }, [productId, referrer]);
}
```

This will ensure that the consumer of the custom hook can optimize their own code when it's needed.

## useMemo
The `useMemo` hook will let you cache the value of a function so that it doesn't change between renders. This is useful when you want to avoid recalculating the value of a function on every render.

```javascript
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
```

The first parameter is the function you want to memoize and the second parameter is an array of dependencies. If any of the dependencies change the function will be re-calculated.

For example if you need to run a filter on your data and you don't want to re-run the filter on every render you can use the `useMemo` hook.

```javascript
function ProductList({ products, filter }) {
  const filteredProducts = useMemo(() => products.filter(filter), [products, filter]);

  return (
    <ul>
      {filteredProducts.map((product) => (
        <li key={product.id}>{product.name}</li>
      ))}
    </ul>
  );
}
```

## useTransition
The `useTransition` hook allows you to update the state without blocking the UI from rendering

```javascript
import { useTransition } from 'react';

const [isPending, startTransition] = useTransition();
```

The `useTransition` hook returns an array with two values. The first value is a boolean that indicates if the transition is pending and the second value is a function that you can use to start the transition.

This hook should be used when you want to update the state without blocking the UI from rendering. For example if you have a list of items that you want to update and you don't want the UI to block while the items are being updated you can use the `useTransition` hook.

```javascript
function List({ items }) {
  const [isPending, startTransition] = useTransition();
  const [newItems, setNewItems] = useState([]);

  const handleClick = () => {
    startTransition(() => {
      setNewItems([...items, 'new item']);
    });
  };

  return (
    <div>
      <ul>
        {items.map((item) => (
          <li key={item}>{item}</li>
        ))}
      </ul>
      {isPending && <p>Loading...</p>}
      <button onClick={handleClick}>Add Item</button>
    </div>
  );
}
```


## useDeferredValue
The `useDeferredValue` hook is used to defer the value of a resource like a promise. This hook must be called from inside a component or another hook.

```javascript
import { useDeferredValue } from 'react';

function MessageComponent({ messagePromise }) {
  const deferredMessage = useDeferredValue(messagePromise);
  return <Message message={deferredMessage} />;
}
```

This can be used when you need to defer the value to help improve the performance of the application. For example if you have a message that is being fetched from an API and you don't want to show the message until it has been fetched you can use the `useDeferredValue` hook.

A good example of using this is inside a `<Suspense>` component when you want to fetch a new value, it will mean you can continue to show the old value or the fallback until the new value is ready.

```javascript
function MessageComponent({ messagePromise }) {
  const deferredMessage = useDeferredValue(messagePromise);
  return (
    <Suspense fallback={<Loading />}>
      <Message message={deferredMessage} />
    </Suspense>
  );
}
```

## useRef
The `useRef` hook is used to create a mutable object that persists for the lifetime of the component. This is useful when you want to store a reference to a DOM element or a value that doesn't change between renders.

```javascript
import { useRef } from 'react';

const ref = useRef(initialValue);
```

For example if you want to store a reference to a DOM element you can use the `useRef` hook.

```javascript
function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    inputEl.current.focus();
  };

  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}
```

## useImperativeHandle
`useImperativeHandle` is a hook that allows you to customize the instance value that is exposed to parent components when using `ref`. This is useful when you want to expose a custom API to the parent component.

```javascript
import { useImperativeHandle, forwardRef } from 'react';

const FancyInput = forwardRef((props, ref) => {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    },
  }));

  return <input ref={inputRef} />;
});
```

This hook should be used when you want to expose a custom API to the parent component. For example if you have a custom input component that you want to expose a focus method to the parent component you can use the `useImperativeHandle` hook.

```javascript
function App() {
  const inputRef = useRef();

  return (
    <div>
      <FancyInput ref={inputRef} />
      <button onClick={() => inputRef.current.focus()}>Focus</button>
    </div>
  );
}
```

## useId
`useId` is a hook that generates a unique id for a component. This is useful when you want to generate a unique id for a component that can be used in other components.

```javascript
import { useId } from 'react';

function MyComponent() {
  const id = useId();
  return <div id={id}>Hello World</div>;
}
```

## useDebugValue
The `useDebugValue` will allow you to add a label to the React DevTools for custom hooks. This is useful when you want to provide more information about the custom hook to the developer.

```javascript
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');

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  }, [friendID]);

  return isOnline;
}
```

## useSyncExternalStore
The `useSyncExternalStore` hook is used to sync the state of a component with an external store. This hook must be called from inside a component or another hook.

```javascript
import { useSyncExternalStore } from 'react';

function MessageComponent({ messagePromise }) {
  const [message, setMessage] = useState(null);
  useSyncExternalStore(messagePromise, setMessage);
  return <Message message={message} />;
}
```

The external store will be something like a promise or a value that is being fetched from an API. For example if you have a message that is being fetched from an API and you want to sync the state of the component with the message you can use the `useSyncExternalStore` hook.

## useOptimistic
The `useOptimistic` hook is used to update the UI optimistically before the server responds. This hook must be called from inside a component or another hook.

```javascript
import { useOptimistic } from 'react';

function MessageComponent({ messagePromise }) {
  const [message, setMessage] = useState(null);
  const optimisticMessage = useOptimistic(messagePromise, setMessage);
  return <Message message={optimisticMessage} />;
}
```

This function is useful when you want to update the UI optimistically before the server responds. For example if you have a message that is being sent to the server and you want to show the message before the server responds you can use the `useOptimistic` hook.

## use
The `use` hook used to read a value of a resource like a promise. This hook must be called from inside a component or another hook.

```javascript
import { use } from 'react';

function MessageComponent({ messagePromise }) {
  const message = use(messagePromise);
  const theme = use(ThemeContext);
}
```

## Custom Hooks
There are a few ways to reuse functionality in React.

- Reuse markup - use a component
- Reuse vanilla JS logic - use a function
- Reuse React logic - use a custom hook

Custom hooks are JavaScript functions that use React hooks. They can be used to share logic between components.

```javascript
import { useState, useEffect } from 'react';

function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch(url)
      .then((response) => response.json())
      .then((data) => {
        setData(data);
        setLoading(false);
      });
  }, [url]);

  return { data, loading };
}
```

You can then use the custom hook in your components.

```javascript
function App() {
  const { data, loading } = useFetch('https://api.example.com/data');

  if (loading) {
    return <div>Loading...</div>;
  }

  return <div>{data}</div>;
}
```

Custom hooks can be used to encapsulate logic that is shared between components. They can be used to separate concerns and make your code more modular and reusable.

## Further Reading
- [React Hooks API Reference](https://react.dev/reference/react/hooks)