JavaScript and React course · Module 5: useEffect and Custom Hooks

References with useRef

13 min read
In this lesson8

On our cosmic journey through React, sometimes we need to maintain certain data or references to DOM elements that persist between renders but don't cause the component to re-render. The useRef hook is like a storage compartment on a spaceship - it lets you store important things that don't change with every maneuver of the ship.

What is useRef?

The useRef hook is one of the built-in hooks in React that:

  1. Creates a mutable reference field that survives throughout the entire component lifecycle
  2. Does not cause component re-rendering when its value changes
  3. Can be used to store references to DOM elements or any other values

Basic syntax:

1const refContainer = useRef(initialValue);

The returned object refContainer has a .current property that is initialized with the initial value (initialValue). This value can be changed without triggering a re-render.

Main use cases for useRef

1. Accessing DOM elements

One of the most popular uses of useRef is gaining direct access to DOM nodes:

1function SpaceshipControls() {
2  // Creating reference
3  const thrusterControlRef = useRef(null);
4
5  // Function using reference to DOM element
6  const focusThrusterControl = () => {
7    // Access DOM element through .current property
8    thrusterControlRef.current.focus();
9  };
10
11  return (
12    <div className="control-panel">
13      <h2>Thruster Control Panel</h2>
14
15      {/* Przypisanie referencji do elementu input */}
16      <input
17        ref={thrusterControlRef}
18        type="range"
19        min="0"
20        max="100"
21        className="thruster-slider"
22      />
23
24      <button onClick={focusThrusterControl}>
25        Activate thrust control
26      </button>
27    </div>
28  );
29}

In this example:

  1. We create a reference thrusterControlRef using useRef(null)
  2. We assign this reference to the input element using the ref attribute
  3. We use the reference to call the focus() method on the DOM element

2. Storing mutable values between renders

useRef can also be used to store values that:

  • Should not cause a re-render when changed
  • Must remain stable throughout the component's lifecycle
1function MissionTimer() {
2  const [seconds, setSeconds] = useState(0);
3  const timerIdRef = useRef(null);
4
5  // Function to start the timer
6  const startTimer = () => {
7    if (timerIdRef.current !== null) return; // Avoid duplicate timers
8
9    timerIdRef.current = setInterval(() => {
10      setSeconds(s => s + 1);
11    }, 1000);
12  };
13
14  // Function to stop the timer
15  const stopTimer = () => {
16    clearInterval(timerIdRef.current);
17    timerIdRef.current = null;
18  };
19
20  // Clean up interval on unmount
21  useEffect(() => {
22    return () => {
23      if (timerIdRef.current !== null) {
24        clearInterval(timerIdRef.current);
25      }
26    };
27  }, []);
28
29  return (
30    <div className="mission-timer">
31      <p>Mission time: {seconds} seconds</p>
32      <button onClick={startTimer}>Start</button>
33      <button onClick={stopTimer}>Stop</button>
34    </div>
35  );
36}

In this example, we use useRef to store the interval ID. This is an ideal use case because:

  • We don't need a re-render when the interval ID changes
  • We need to preserve the ID between renders to be able to stop the interval
  • This value is not used directly in the rendered JSX

3. Tracking previous values

One interesting use of useRef is tracking previous values of props or state:

1function usePrevious(value) {
2  const ref = useRef();
3
4  // Update ref.current after rendering
5  useEffect(() => {
6    ref.current = value;
7  }, [value]);
8
9  // Returns the previous value (undefined during the first render)
10  return ref.current;
11}
12
13function AsteroidTracker({ asteroidId }) {
14  const [asteroidData, setAsteroidData] = useState(null);
15  // Track the previous ID
16  const previousAsteroidId = usePrevious(asteroidId);
17
18  // Effect that logs the change of the observed asteroid
19  useEffect(() => {
20    if (previousAsteroidId !== asteroidId) {
21      console.log(`Tracking changed from asteroid ${previousAsteroidId} to ${asteroidId}`);
22      // Here we could fetch new data about the asteroid
23    }
24  }, [asteroidId, previousAsteroidId]);
25
26  // Rendering...
27}

The usePrevious hook uses useRef to store the previous value. The reference is updated in useEffect, which runs after rendering, so the value stored in the ref is always one render cycle "behind" the current value.

4. Avoiding unnecessary effects

useRef can help with optimization by avoiding unnecessary effects:

1function TelemetryMonitor({ shipId }) {
2  const [data, setData] = useState(null);
3  const [isConnected, setIsConnected] = useState(false);
4
5  // Store the ID to avoid unnecessary connection changes
6  const lastShipIdRef = useRef(shipId);
7
8  useEffect(() => {
9    // Check if the ship ID actually changed
10    if (lastShipIdRef.current === shipId && isConnected) {
11      console.log('Ship hasn't changed, skipping reconnection');
12      return;
13    }
14
15    // Update ref with the new ID
16    lastShipIdRef.current = shipId;
17
18    // Establish connection...
19    console.log(`Connecting to ship telemetry ${shipId}...`);
20
21    const connection = telemetryService.connect(shipId);
22    setIsConnected(true);
23
24    connection.onData(newData => {
25      setData(newData);
26    });
27
28    return () => {
29      console.log(`Disconnecting ship telemetry ${shipId}...`);
30      connection.disconnect();
31      setIsConnected(false);
32    };
33  }, [shipId]);
34
35  // Rendering...
36}

useRef vs useState - when to use which?

The difference between useRef and useState is crucial to understand:

AspectuseRefuseState
Powoduje re-renderingNoYes
Preserves value between rendersYesYes
Accessing valueThrough the .current propertyDirectly by reading the state variable
Updating valueDirect mutation ref.current = newValueThrough updater function setState(newValue)
When to use?For values that don't affect render outputFor values that should trigger re-rendering

When to use useRef:

  • Accessing DOM elements
  • Storing timer and interval IDs
  • Storing previous values
  • Values that change frequently but don't affect UI
  • Storing class instances and objects that shouldn't be recreated on every render

When to use useState:

  • Data that affects what the user sees
  • Values whose changes should cause UI updates
  • State that controls the behavior or appearance of the user interface

Advanced useRef patterns

1. Callback Refs

Sometimes we need greater control over when a ref is assigned. In such cases, we can use a callback ref:

1function SpectrumAnalyzer() {
2  const [frequency, setFrequency] = useState(440);
3  let canvasRef = useRef(null);
4
5  // Callback function for ref, called when ref is assigned or detached
6  const setCanvasRef = useCallback(element => {
7    // element will be null during unmounting
8    if (element) {
9      canvasRef.current = element;
10      // We can now perform initialization after the reference is assigned
11      initializeCanvas(element, frequency);
12    } else {
13      // Cleaning up resources on unmount
14      if (canvasRef.current) {
15        cleanupCanvas(canvasRef.current);
16      }
17      canvasRef.current = null;
18    }
19  }, [frequency]); // Dependency on frequency
20
21  // Function initializing canvas
22  const initializeCanvas = (canvas, freq) => {
23    console.log(`Initializing analyzer for frequency ${freq} Hz`);
24    const ctx = canvas.getContext('2d');
25    // Drawing...
26  };
27
28  return (
29    <div>
30      <canvas
31        ref={setCanvasRef}
32        width="600"
33        height="300"
34      />
35      <input
36        type="range"
37        min="20"
38        max="20000"
39        value={frequency}
40        onChange={e => setFrequency(Number(e.target.value))}
41      />
42      <div>Frequency: {frequency} Hz</div>
43    </div>
44  );
45}

Callback ref gives us more control because it's called both when assigning and when removing the reference.

2. Combining useRef with forwardRef

Sometimes we want to pass a reference to a child component. We can do this using forwardRef:

1// Child component
2const NavigationControl = forwardRef((props, ref) => {
3  return (
4    <div className="nav-control">
5      <label>{props.label}</label>
6      <input
7        ref={ref}
8        type="text"
9        className="nav-input"
10        value={props.value}
11        onChange={props.onChange}
12      />
13    </div>
14  );
15});
16
17// Parent component
18function NavigationPanel() {
19  const destinationInputRef = useRef(null);
20  const [destination, setDestination] = useState('');
21
22  const focusDestination = () => {
23    // We can access the inner input thanks to forwardRef
24    destinationInputRef.current.focus();
25  };
26
27  return (
28    <div className="nav-panel">
29      <h2>Navigation Panel</h2>
30
31      <NavigationControl
32        ref={destinationInputRef}
33        label="Destination:"
34        value={destination}
35        onChange={e => setDestination(e.target.value)}
36      />
37
38      <button onClick={focusDestination}>
39        Set destination
40      </button>
41    </div>
42  );
43}

forwardRef allows a component to receive a reference from its parent and pass it to a specific element inside the component.

3. Storing multiple references

Sometimes we need to track multiple elements, for example a list of elements:

1function StarMap({ stars }) {
2  // Storing map of references to stars
3  const starRefs = useRef({});
4
5  // Function to scroll to a specific star
6  const scrollToStar = (starId) => {
7    if (starRefs.current[starId]) {
8      starRefs.current[starId].scrollIntoView({
9        behavior: 'smooth',
10        block: 'center'
11      });
12    }
13  };
14
15  return (
16    <div className="star-map-container">
17      <div className="star-list">
18        {stars.map(star => (
19          <div
20            key={star.id}
21            // We save the reference to the element in our map
22            ref={el => starRefs.current[star.id] = el}
23            className="star-item"
24          >
25            <h3>{star.name}</h3>
26            <p>Typ: {star.type}</p>
27            <p>Distance: {star.distance} light years</p>
28          </div>
29        ))}
30      </div>
31
32      <div className="star-navigator">
33        <h3>Quick navigation</h3>
34        {stars.map(star => (
35          <button
36            key={star.id}
37            onClick={() => scrollToStar(star.id)}
38          >
39            {star.name}
40          </button>
41        ))}
42      </div>
43    </div>
44  );
45}

In this example, we store a map of references where the key is the star's id and the value is a reference to the corresponding DOM element.

Common pitfalls when using useRef

1. Attempting to render directly from ref.current

Since changing ref.current does not cause a re-render, attempting to use this value directly in JSX can lead to inconsistencies:

1// BAD: Changing ref.current won't cause the interface to update
2function BadCounter() {
3  const countRef = useRef(0);
4
5  const increment = () => {
6    // This change will not cause a re-render
7    countRef.current += 1;
8    console.log(`Counter: ${countRef.current}`); // Will be logged correctly
9  };
10
11  return (
12    <div>
13      <p>Counter: {countRef.current}</p> {/* Won't update after clicking */}
14      <button onClick={increment}>Increment</button>
15    </div>
16  );
17}
18
19// GOOD: Use useState for values that affect rendering
20function GoodCounter() {
21  const [count, setCount] = useState(0);
22
23  const increment = () => {
24    setCount(c => c + 1);
25  };
26
27  return (
28    <div>
29      <p>Counter: {count}</p> {/* Will update after clicking */}
30      <button onClick={increment}>Increment</button>
31    </div>
32  );
33}

2. Forgetting about .current

A common mistake is forgetting to access the value through .current:

1// BAD: Brak .current
2function MissionLog({ onAddLog }) {
3  const logInputRef = useRef(null);
4
5  const handleAddLog = () => {
6    // Error: We forgot about .current
7    const logText = logInputRef.value;
8    onAddLog(logText);
9  };
10
11  return (
12    <div>
13      <input ref={logInputRef} type="text" />
14      <button onClick={handleAddLog}>Add entry</button>
15    </div>
16  );
17}
18
19// GOOD: We use .current
20function MissionLog({ onAddLog }) {
21  const logInputRef = useRef(null);
22
23  const handleAddLog = () => {
24    // Correct usage of .current
25    const logText = logInputRef.current.value;
26    onAddLog(logText);
27    logInputRef.current.value = '';
28  };
29
30  return (
31    <div>
32      <input ref={logInputRef} type="text" />
33      <button onClick={handleAddLog}>Add entry</button>
34    </div>
35  );
36}

3. Improper use of ref for render control

useRef is not the appropriate tool for controlling when a component should render:

1// BAD: Attempting to use ref to control rendering
2function BadRenderControl() {
3  const shouldUpdateRef = useRef(false);
4
5  const forceUpdate = () => {
6    shouldUpdateRef.current = true;
7    // Missing mechanism to force re-rendering
8  };
9
10  console.log('Rendering',shouldUpdateRef.current);
11
12  // This won't work as expected
13  if (shouldUpdateRef.current) {
14    shouldUpdateRef.current = false;
15    // Some operations...
16  }
17
18  return <button onClick={forceUpdate}>Refresh</button>;
19}
20
21// GOOD: Using useState to control rendering
22function GoodRenderControl() {
23  const [update, setUpdate] = useState(0);
24  const didUpdateRef = useRef(false);
25
26  const forceUpdate = () => {
27    setUpdate(u => u + 1); // Triggers re-rendering
28  };
29
30  console.log('Rendering',update);
31
32  useEffect(() => {
33    if (update > 0) {
34      didUpdateRef.current = true;
35      // Operations after update...
36    }
37  }, [update]);
38
39  return <button onClick={forceUpdate}>Refresh</button>;
40}

Best practices with useRef

  1. Use useRef for values that don't affect rendering - if a value change should cause an interface update, use useState.

  2. Be cautious with direct DOM manipulation - DOM manipulation through references should be limited and well thought out.

  3. Initialize refs with appropriate initial values - for example null for DOM references or initial values for counters.

  4. Check if ref.current exists before using it - especially for DOM references that may be null during the first render.

  5. Use useCallback for ref-creating functions - if you're passing a reference function, consider wrapping it in useCallback for optimization.

  6. Avoid overusing refs - although refs are useful, over-relying on them can lead to hard-to-maintain code.

Practical example: Advanced mission video player

Below is a more complex example that demonstrates various aspects of using useRef:

1import React, { useState, useRef, useEffect, useCallback } from 'react';
2
3function MissionVideoPlayer({ videoUrl, onProgress }) {
4  const [isPlaying, setIsPlaying] = useState(false);
5  const [duration, setDuration] = useState(0);
6  const [currentTime, setCurrentTime] = useState(0);
7  const [volume, setVolume] = useState(0.7);
8
9  // References
10  const videoRef = useRef(null);          // Reference to video element
11  const progressBarRef = useRef(null);    // Reference to progress bar
12  const animationFrameRef = useRef(null); // requestAnimationFrame ID for updating time
13  const isMountedRef = useRef(true);      // Flag indicating whether the component is mounted
14  const lastUpdateTimeRef = useRef(0);    // Time of the last update for throttling
15
16  // Function initializing video metadata
17  const handleLoadedMetadata = () => {
18    if (videoRef.current) {
19      setDuration(videoRef.current.duration);
20      videoRef.current.volume = volume;
21    }
22  };
23
24  // Function updating progress bar
25  const updateProgressBar = useCallback(() => {
26    if (!videoRef.current || !isMountedRef.current) return;
27
28    const now = Date.now();
29    if (now - lastUpdateTimeRef.current > 200) { // Throttling to 5 updates per second
30      setCurrentTime(videoRef.current.currentTime);
31      onProgress(videoRef.current.currentTime);
32      lastUpdateTimeRef.current = now;
33    }
34
35    // Continue updating if video is playing
36    if (videoRef.current.paused) return;
37    animationFrameRef.current = requestAnimationFrame(updateProgressBar);
38  }, [onProgress]);
39
40  // Start or pause playback
41  const togglePlayPause = () => {
42    if (!videoRef.current) return;
43
44    if (videoRef.current.paused) {
45      videoRef.current.play();
46      setIsPlaying(true);
47      animationFrameRef.current = requestAnimationFrame(updateProgressBar);
48    } else {
49      videoRef.current.pause();
50      setIsPlaying(false);
51      if (animationFrameRef.current) {
52        cancelAnimationFrame(animationFrameRef.current);
53      }
54    }
55  };
56
57  // Changing playback position after clicking progress bar
58  const handleProgressBarClick = (e) => {
59    if (!progressBarRef.current || !videoRef.current) return;
60
61    const rect = progressBarRef.current.getBoundingClientRect();
62    const position = (e.clientX - rect.left) / rect.width;
63    const newTime = duration * Math.max(0, Math.min(1, position));
64
65    videoRef.current.currentTime = newTime;
66    setCurrentTime(newTime);
67  };
68
69  // Changing volume
70  const handleVolumeChange = (e) => {
71    const newVolume = parseFloat(e.target.value);
72    setVolume(newVolume);
73
74    if (videoRef.current) {
75      videoRef.current.volume = newVolume;
76    }
77  };
78
79  // Effect cleaning up animationFrame and setting mounting flag
80  useEffect(() => {
81    return () => {
82      isMountedRef.current = false;
83      if (animationFrameRef.current) {
84        cancelAnimationFrame(animationFrameRef.current);
85      }
86    };
87  }, []);
88
89  // Formatting time (mm:ss)
90  const formatTime = (timeInSeconds) => {
91    const minutes = Math.floor(timeInSeconds / 60);
92    const seconds = Math.floor(timeInSeconds % 60);
93    return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
94  };
95
96  return (
97    <div className="mission-video-player">
98      <video
99        ref={videoRef}
100        src={videoUrl}
101        onLoadedMetadata={handleLoadedMetadata}
102        onEnded={() => setIsPlaying(false)}
103      />
104
105      <div className="controls">
106        <button onClick={togglePlayPause}>
107          {isPlaying ? 'Pause' : 'Play'}
108        </button>
109
110        <div className="time-display">
111          {formatTime(currentTime)} / {formatTime(duration)}
112        </div>
113
114        <div
115          ref={progressBarRef}
116          className="progress-bar"
117          onClick={handleProgressBarClick}
118        >
119          <div
120            className="progress-fill"
121            style={{ width: `${(currentTime / duration) * 100}%` }}
122          />
123        </div>
124
125        <div className="volume-control">
126          <label>Volume:</label>
127          <input
128            type="range"
129            min="0"
130            max="1"
131            step="0.05"
132            value={volume}
133            onChange={handleVolumeChange}
134          />
135        </div>
136      </div>
137    </div>
138  );
139}

This example demonstrates several advanced aspects of working with useRef:

  1. Multiple different uses of references:

    • Access to DOM element (videoRef, progressBarRef)
    • Storing animation ID (animationFrameRef)
    • Mounting flag (isMountedRef)
    • Tracking values for throttling (lastUpdateTimeRef)
  2. Integration of references with effects and callbacks

  3. Direct manipulation of the video element through its API

  4. Throttling state updates using time references

Summary

The useRef hook is an essential tool in the React toolkit, particularly useful for:

  • Storing references to DOM elements
  • Maintaining values between renders without triggering re-rendering
  • Tracking previous values
  • Storing timer, interval, and requestAnimationFrame IDs
  • Storing flags and values that don't directly affect the interface

The key to effectively using useRef is understanding when to use it instead of useState. The fundamental difference is that useRef does not cause a re-render when its value changes, making it the ideal choice for operations that shouldn't affect the rendering cycle.

On our cosmic journey through React, useRef is like a storage compartment on the ship - a place where you can store things you need to have on hand, but that don't require the attention of the entire crew every time they change.

Code for this lesson: App.jsx
1import React, { useState, useRef, useEffect } from 'react';
2
3// useRef for storing references to DOM elements
4function DOMReferenceDemo() {
5  const inputRef = useRef(null);
6  const canvasRef = useRef(null);
7
8  const focusInput = () => {
9    inputRef.current?.focus();
10  };
11
12  const drawOnCanvas = () => {
13    const canvas = canvasRef.current;
14    if (!canvas) return;
15
16    const ctx = canvas.getContext('2d');
17    ctx.clearRect(0, 0, canvas.width, canvas.height);
18
19    // Drawing stars
20    for (let i = 0; i < 50; i++) {
21      const x = Math.random() * canvas.width;
22      const y = Math.random() * canvas.height;
23      const radius = Math.random() * 2;
24
25      ctx.beginPath();
26      ctx.arc(x, y, radius, 0, Math.PI * 2);
27      ctx.fillStyle = `rgba(255, 255, 255, ${Math.random()})`;
28      ctx.fill();
29    }
30
31    // Drawing a planet
32    ctx.beginPath();
33    ctx.arc(canvas.width / 2, canvas.height / 2, 30, 0, Math.PI * 2);
34    ctx.fillStyle = '#61dafb';
35    ctx.fill();
36  };
37
38  return (
39    <div className="demo-card">
40      <h2>DOM References</h2>
41
42      <div className="input-section">
43        <input ref={inputRef} type="text" placeholder="Click the button below" />
44        <button onClick={focusInput}>Focus the input</button>
45      </div>
46
47      <div className="canvas-section">
48        <canvas ref={canvasRef} width={280} height={150} />
49        <button onClick={drawOnCanvas}>Draw space</button>
50      </div>
51    </div>
52  );
53}
54
55// useRef for storing values between renders
56function PreviousValueDemo() {
57  const [count, setCount] = useState(0);
58  const prevCountRef = useRef(0);
59
60  useEffect(() => {
61    prevCountRef.current = count;
62  });
63
64  return (
65    <div className="demo-card">
66      <h2>Previous value</h2>
67      <div className="values">
68        <div className="value-box">
69          <span className="label">Current:</span>
70          <span className="number current">{count}</span>
71        </div>
72        <div className="value-box">
73          <span className="label">Previous:</span>
74          <span className="number previous">{prevCountRef.current}</span>
75        </div>
76      </div>
77      <button onClick={() => setCount(c => c + 1)}>Increase</button>
78    </div>
79  );
80}
81
82// useRef for measuring elements
83function MeasurementDemo() {
84  const boxRef = useRef(null);
85  const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
86  const [boxSize, setBoxSize] = useState(100);
87
88  const measureBox = () => {
89    if (boxRef.current) {
90      const rect = boxRef.current.getBoundingClientRect();
91      setDimensions({ width: rect.width, height: rect.height });
92    }
93  };
94
95  return (
96    <div className="demo-card">
97      <h2>Element measurements</h2>
98      <div
99        ref={boxRef}
100        className="measure-box"
101        style={{ width: boxSize, height: boxSize }}
102      >
103        {boxSize}x{boxSize}
104      </div>
105      <div className="measurement-info">
106        Measured: {dimensions.width.toFixed(0)} x {dimensions.height.toFixed(0)}
107      </div>
108      <div className="controls">
109        <button onClick={() => setBoxSize(s => Math.max(50, s - 20))}></button>
110        <button onClick={measureBox}>Measure</button>
111        <button onClick={() => setBoxSize(s => Math.min(200, s + 20))}></button>
112      </div>
113    </div>
114  );
115}
116
117// useRef for storing timeouts/intervals
118function TimerRefDemo() {
119  const [time, setTime] = useState(0);
120  const [isRunning, setIsRunning] = useState(false);
121  const intervalRef = useRef(null);
122
123  const start = () => {
124    if (intervalRef.current) return;
125
126    setIsRunning(true);
127    intervalRef.current = setInterval(() => {
128      setTime(t => t + 1);
129    }, 100);
130  };
131
132  const stop = () => {
133    if (intervalRef.current) {
134      clearInterval(intervalRef.current);
135      intervalRef.current = null;
136    }
137    setIsRunning(false);
138  };
139
140  const reset = () => {
141    stop();
142    setTime(0);
143  };
144
145  // Cleanup on unmount
146  useEffect(() => {
147    return () => {
148      if (intervalRef.current) {
149        clearInterval(intervalRef.current);
150      }
151    };
152  }, []);
153
154  const formatTime = (t) => {
155    const seconds = Math.floor(t / 10);
156    const decimals = t % 10;
157    return `${seconds}.${decimals}s`;
158  };
159
160  return (
161    <div className="demo-card">
162      <h2>⏱Timer with useRef</h2>
163      <div className="timer-display">{formatTime(time)}</div>
164      <div className="controls">
165        <button onClick={start} disabled={isRunning}>▶Start</button>
166        <button onClick={stop} disabled={!isRunning}>⏸Pause</button>
167        <button onClick={reset}>Reset</button>
168      </div>
169      <p className="hint">Interval ID stored in useRef</p>
170    </div>
171  );
172}
173
174function App() {
175  return (
176    <div className="app">
177      <h1>useRef Hook</h1>
178      <p className="subtitle">References and values outside the render cycle</p>
179      <div className="demos">
180        <DOMReferenceDemo />
181        <PreviousValueDemo />
182        <MeasurementDemo />
183        <TimerRefDemo />
184      </div>
185    </div>
186  );
187}
188
189export default App;

Spotted a mistake in this lesson?

Check yourself

Answer the questions from this lesson. Pick an answer to see right away whether it is correct.

  1. 1. What does the dependency array in useEffect contain?

  2. 2. Which of the following is an example of a side effect in React?

Hands-on tasks in the game

  • Horizontal ordering

    Arrange the useRef declaration elements from left to right:

  • Vertical ordering

    Order the steps for correctly using async/await inside useEffect:

  • Click in order

    Arrange async operations in useEffect:

  • Code editor

    Geolocation Position Tracking

Useful articles