Robimy to, co w lekcji poprzedniej, ale tym razem używając frameworka React i jego hooków – useState i useEffect. Do dzieła.
Ok, przypomnijmy sobie, co robi useEffect z pustą tablicą zależności:
useEffect(() => {
fetch("https://pokeapi.co/api/v2/type/3")
}, []);
Z pustą – działa jeden raz, po podłączeniu. Ok, a coś takiego jak cleanup function pamiętamy?
Składnia taka:
import React, { useEffect } from "React";
useEffect(() => {
// Your effect
return () => {
// Cleanup
};
}, []);
Czyli w returnie useEffect podajemy funkcję, która jest funkcją cleanup. Przykład z abortcontrollerem:
useEffect(() => {
//create the abort controller
let controller = new AbortController();
(async () => {
try {
const response = await fetch(APIEndpoint, {
// attach the controller to the request
signal: controller.signal,
});
// add the success response to a state value
} catch (e) {
// Handle the error
}
})();
//abort the request when the component umounts
return () => controller?.abort();
}, []);
Ok, abort controllera znamy z eventów, ale dla różnych fetchów też go stosować można.
Przykład cleanupu z timeout:
useEffect(() => {
let timerId = setTimeout(() => {
// perform an action like state update
timerId = null;
}, 5000);
// clear the timer when component unmouts
return () => clearTimeout(timerId);
}, []);
Do takich rzeczy używamy funkcji cleanup. Ok, mam nadzieję, że pamiętamy też o useState oraz że każde wywołanie setState triggeruje re-render.
Dobra, zaczynamy, przykład z dokumentacji Reacta btw:
import { useState, useEffect } from 'react';
export default function App() {
const [position, setPosition] = useState({ x: 0, y: 0 });
Na razie chyba rozumiemy. Initial state na 0 dla x,y, position to ten stan (do czytania), setPosition ustawia nowy stan i triggeruje re-render.
To teraz useEffect:
import { useState, useEffect } from 'react';
export default function App() {
const [position, setPosition] = useState({ x: 0, y: 0 });
useEffect(() => {
function handleMove(e) {
setPosition({ x: e.clientX, y: e.clientY });
}
window.addEventListener('pointermove', handleMove);
return () => {
window.removeEventListener('pointermove', handleMove);
};
}, []);
Jest tak:
- pusta tablica zależności, więc useEffect rusza jeden raz
- funcja handleMove używa setPosition więc wymusza re-render
- useEffect nadaje się do takich zastosowań, jak globalne eventy przeglądarki i inne efekty
- jak dodaliśmy event listener, to funkcja cleanup go musi posprzątać
No dobra, a gdzie będzie ten nasz dot? Cóż, tym się zajmie zwracany JSX:
import { useState, useEffect } from 'react';
export default function App() {
const [position, setPosition] = useState({ x: 0, y: 0 });
useEffect(() => {
function handleMove(e) {
setPosition({ x: e.clientX, y: e.clientY });
}
window.addEventListener('pointermove', handleMove);
return () => {
window.removeEventListener('pointermove', handleMove);
};
}, []);
return (
<div style={{
position: 'absolute',
backgroundColor: 'pink',
borderRadius: '50%',
opacity: 0.6,
transform: `translate(${position.x}px, ${position.y}px)`,
pointerEvents: 'none',
left: -20,
top: -20,
width: 40,
height: 40,
}} />
);
}
Jak widać myślniki zastąpione wielką literą, uchodzi brak jednostek, property values jako string (chyba że numeryczny typ bez jednostki), daje radę użyć interpolacji do naszego transforma.
Więcej Reacta niedługo.