---
source_hash: "53c261df"
title: "Chapter 3: useRef"
weight: 420
---

# Chapter 3: useRef

## Two Uses of useRef

useRef has two main uses:

1. **Access a DOM element** (input, div, etc.)
2. **Store a persistent value** between renders without triggering a re-render

## Access the DOM

### Example: Focus on an Input

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

function SearchForm() {
    const inputRef = useRef(null);

    const handleClick = () => {
        inputRef.current.focus();
    };

    return (
        <div>
            <input ref={inputRef} type="text" placeholder="Search..." />
            <button onClick={handleClick}>Focus</button>
        </div>
    );
}
```

### How It Works

1. `useRef(null)` creates an object `{ current: null }`
2. `ref={inputRef}` tells React to put the DOM element in `inputRef.current`
3. After rendering, `inputRef.current` contains the `<input>` element

### Example: Scroll to an Element

```javascript
function ScrollToSection() {
    const sectionRef = useRef(null);

    const scrollToSection = () => {
        sectionRef.current.scrollIntoView({ behavior: 'smooth' });
    };

    return (
        <div>
            <button onClick={scrollToSection}>Go to section</button>

            {/* ... lots of content ... */}

            <section ref={sectionRef}>
                <h2>Target Section</h2>
            </section>
        </div>
    );
}
```

### Example: Measure an Element

```javascript
function MeasuredBox() {
    const boxRef = useRef(null);
    const [dimensions, setDimensions] = useState({ width: 0, height: 0 });

    useEffect(() => {
        if (boxRef.current) {
            const { width, height } = boxRef.current.getBoundingClientRect();
            setDimensions({ width, height });
        }
    }, []);

    return (
        <div>
            <div ref={boxRef} style={{ padding: 20, background: '#eee' }}>
                Box content
            </div>
            <p>Dimensions: {dimensions.width} x {dimensions.height}</p>
        </div>
    );
}
```

## Store a Persistent Value

### Difference from useState

| Criteria | useState | useRef |
| --- | --- | --- |
| Persists between renders | Yes | Yes |
| Triggers a re-render | Yes | No |
| Access value | `state` | `ref.current` |

### Example: Count Renders

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

    // Incremented on every render, without causing a re-render
    renderCount.current += 1;

    return (
        <div>
            <p>Count: {count}</p>
            <p>Renders: {renderCount.current}</p>
            <button onClick={() => setCount(c => c + 1)}>+1</button>
        </div>
    );
}
```

### Example: Previous Value

```javascript
function usePrevious(value) {
    const ref = useRef();

    useEffect(() => {
        ref.current = value;
    }, [value]);

    return ref.current;
}

function Counter() {
    const [count, setCount] = useState(0);
    const previousCount = usePrevious(count);

    return (
        <div>
            <p>Current: {count}</p>
            <p>Previous: {previousCount}</p>
            <button onClick={() => setCount(c => c + 1)}>+1</button>
        </div>
    );
}
```

### Example: Avoid useEffect Re-executions

```javascript
function Timer({ onTick }) {
    const onTickRef = useRef(onTick);

    // Update ref without triggering a re-render
    useEffect(() => {
        onTickRef.current = onTick;
    }, [onTick]);

    useEffect(() => {
        const interval = setInterval(() => {
            onTickRef.current();  // Always uses the latest version
        }, 1000);

        return () => clearInterval(interval);
    }, []);  // No need for onTick in dependencies
}
```

## Use Case with Uncontrolled Forms

Sometimes, it's simpler to read the value from the DOM directly:

```javascript
function UncontrolledForm() {
    const nameRef = useRef(null);
    const emailRef = useRef(null);

    const handleSubmit = (e) => {
        e.preventDefault();
        console.log({
            name: nameRef.current.value,
            email: emailRef.current.value
        });
    };

    return (
        <form onSubmit={handleSubmit}>
            <input ref={nameRef} type="text" placeholder="Name" />
            <input ref={emailRef} type="email" placeholder="Email" />
            <button type="submit">Submit</button>
        </form>
    );
}
```

**Note**: controlled forms (with useState) are generally preferred as they allow real-time validation.

## Warning: Do not read/write during rendering

```javascript
// INCORRECT
function BadComponent() {
    const ref = useRef(0);
    ref.current += 1;  // Modification during rendering
    return <div>{ref.current}</div>;
}

// CORRECT - modify in useEffect or event handler
function GoodComponent() {
    const ref = useRef(0);

    useEffect(() => {
        ref.current += 1;  // OK in useEffect
    });

    const handleClick = () => {
        ref.current += 1;  // OK in handler
    };

    return <div>...</div>;
}
```

## Exercises

### Exercise 1: Auto-focus on Mount

Create a login form that automatically focuses on the email field on load.

**Solution:**

```javascript
function LoginForm() {
    const emailRef = useRef(null);

    useEffect(() => {
        emailRef.current.focus();
    }, []);

    return (
        <form>
            <input ref={emailRef} type="email" placeholder="Email" />
            <input type="password" placeholder="Password" />
            <button type="submit">Login</button>
        </form>
    );
}
```

### Exercise 2: Stopwatch with Pause

Create a stopwatch with Start/Pause/Reset buttons.

**Solution:**

```javascript
function Stopwatch() {
    const [time, setTime] = useState(0);
    const [isRunning, setIsRunning] = useState(false);
    const intervalRef = useRef(null);

    useEffect(() => {
        if (isRunning) {
            intervalRef.current = setInterval(() => {
                setTime(t => t + 1);
            }, 1000);
        }

        return () => {
            if (intervalRef.current) {
                clearInterval(intervalRef.current);
            }
        };
    }, [isRunning]);

    const start = () => setIsRunning(true);
    const pause = () => setIsRunning(false);
    const reset = () => {
        setIsRunning(false);
        setTime(0);
    };

    return (
        <div>
            <p>{time} seconds</p>
            <button onClick={start} disabled={isRunning}>Start</button>
            <button onClick={pause} disabled={!isRunning}>Pause</button>
            <button onClick={reset}>Reset</button>
        </div>
    );
}
```

## Key Points to Remember

1. **useRef for DOM**: `ref={myRef}` then `myRef.current`
2. **useRef to persist**: stores a value without re-render
3. **Do not modify during rendering**: only in useEffect or events
4. **Value in `.current`**: `ref.current` and not `ref`

[<- Previous Chapter](/training/module3-hooks-fondamentaux/useeffect) | [Back to module](/training/module3-hooks-fondamentaux) | [Next Chapter: useContext ->](/training/module3-hooks-fondamentaux/usecontext)
