# react-hooks/set-state-in-effect

> Disallow calling a state setter directly in the body of an effect

An effect runs after React has rendered. If its body sets state right away, React renders the component a second time right after the first. Most of the time you don't need the state at all, because you can compute a value that depends only on props and other state while rendering.

The rule checks `useEffect`, `useLayoutEffect` and `useInsertionEffect`. Setting state later is fine, like in an event listener, a timer, or after a `fetch()`.

{/* Generated by src/codegen/lint-rule-docs.ts. Do not edit this section. */}

## Configure

Off by default. To turn it on:

```toml bunfig.toml
[lint.rules]
"react-hooks/set-state-in-effect" = "error" # or "warn"
```

{/* End of the generated section. */}

## Examples

Reported:

{/* bad */}
```jsx
import { useEffect, useState } from "react";

function FullName({ first, last }) {
  const [fullName, setFullName] = useState("");

  useEffect(() => {
    setFullName(first + " " + last);
  }, [first, last]);

  return <span>{fullName}</span>;
}
```

Moving the call into a helper doesn't change anything:

{/* bad */}
```jsx
import { useEffect, useState } from "react";

function Search({ query }) {
  const [results, setResults] = useState([]);
  const clear = () => setResults([]);

  useEffect(() => {
    clear();
  }, [query]);

  return <List items={results} />;
}
```

Not reported:

{/* good */}
```jsx
function FullName({ first, last }) {
  const fullName = first + " " + last;
  return <span>{fullName}</span>;
}
```

This effect subscribes to something outside React, and only sets state when that changes:

{/* good */}
```jsx
import { useEffect, useState } from "react";

function OnlineStatus() {
  const [online, setOnline] = useState(true);

  useEffect(() => {
    const update = () => setOnline(navigator.onLine);
    window.addEventListener("online", update);
    return () => window.removeEventListener("online", update);
  }, []);

  return <span>{online ? "Online" : "Offline"}</span>;
}
```

The same goes for data that arrives later:

{/* good */}
```jsx
import { useEffect, useState } from "react";

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

  useEffect(() => {
    fetch("/users/" + id)
      .then(response => response.json())
      .then(data => setUser(data));
  }, [id]);

  return <span>{user?.name}</span>;
}
```

You only know a ref's value after rendering, so storing it in state is allowed:

{/* good */}
```jsx
import { useLayoutEffect, useRef, useState } from "react";

function Tooltip({ text }) {
  const ref = useRef(null);
  const [height, setHeight] = useState(0);

  useLayoutEffect(() => {
    setHeight(ref.current.offsetHeight);
  }, []);

  return <div ref={ref} style={{ top: -height }}>{text}</div>;
}
```
