# bun/react-prefer-updater-function

> Prefer `setItems(previous => ..)` to a new array or object made from the state of the last render

The `items` in `const [items, setItems] = useState([])` is the state as of the last render. If `setItems([...items, item])` runs after an `await`, in a timer, or twice before the next render, `items` is out of date and the other update is lost. A function gets the current state: `setItems(previous => [...previous, item])`.

The rule reports a setter that's called with an array or object literal that spreads its own state.

{/* 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]
"bun/react-prefer-updater-function" = "error" # or "warn"
```

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

## Examples

Reported:

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

function Cart() {
  const [items, setItems] = useState([]);

  async function add(id) {
    const item = await loadItem(id);
    setItems([...items, item]);
  }
  // ...
}
```

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

function Profile() {
  const [form, setForm] = useState({ name: "", email: "" });

  return <input value={form.name} onChange={event => setForm({ ...form, name: event.target.value })} />;
}
```

Not reported:

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

function Cart() {
  const [items, setItems] = useState([]);

  async function add(id) {
    const item = await loadItem(id);
    setItems(previous => [...previous, item]);
  }
  // ...
}
```

If neither name comes from a `useState()` in the file, for example because both are props, the rule goes by the names. `setRows` is treated as the setter for `rows`:

{/* bad */}
```jsx
function AddRow({ rows, setRows }) {
  return <button onClick={() => setRows([...rows, { id: rows.length }])}>Add</button>;
}
```

Spreading something else is fine:

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

function Search({ defaults }) {
  const [filters, setFilters] = useState({});

  return <button onClick={() => setFilters({ ...defaults })}>Reset</button>;
}
```
