Kontynuacja poznawania hooka useState, tym razem patrzymy na initializer function oraz stan jako obiekt. Do dzieła.
Ok, oto przykład initializer function:
import { useState } from 'react';
function createInitialTodos() {
const initialTodos = [];
for (let i = 0; i < 50; i++) {
initialTodos.push({
id: i,
text: 'Item ' + (i + 1)
});
}
return initialTodos;
}
export default function TodoList() {
const [todos, setTodos] = useState(createInitialTodos);
const [text, setText] = useState('');
return (
<>
<input
value={text}
onChange={e => setText(e.target.value)}
/>
<button onClick={() => {
setText('');
setTodos([{
id: todos.length,
text: text
}, ...todos]);
}}>Add</button>
<ul>
{todos.map(item => (
<li key={item.id}>
{item.text}
</li>
))}
</ul>
</>
);
}
Ta funkcja znajduje się powyżej poziomu RFC, więc nie jest definiowana od zera przy każdym re-renderze. Jest również w odpowiedni sposób przekazana, tak, aby była używana tylko za pierwszym renderem.
A jak wyglądałby nieodpowiedni sposób? Tak:
export default function TodoList() {
const [todos, setTodos] = useState(createInitialTodos());
const [text, setText] = useState('');
Co do samego trzymania funkcji powyżej RFC – nie musimy. Możemy wewnątrz RFC, ale wtedy zróbmy im useCallback, jeżeli definiowanie od zera będzie dla nas uciążliwe (nie chodzi tu tylko o optymalizację, definiowanie od zera tej samej funkcji i przekazywanie jej jako prop to de facto przekazywanie innej i wymuszanie re-rendera na dziecku, choć teoretycznie nic się nie zmienia).
Ok, teraz initializer function w praktyce – custom hook useLocalStorage:
import { useState, useEffect } from "react";
export function useLocalStorageState(initialState, key) {
const [value, setValue] = useState(function () {
const storedValue = localStorage.getItem(key);
return storedValue ? JSON.parse(storedValue) : initialState;
});
useEffect(
function () {
localStorage.setItem(key, JSON.stringify(value));
},
[value, key]
);
return [value, setValue];
}
Teraz tak:
- JSON stringify pozwala zamienić obiekt/tablicę na string w formacie JSON (a zatem możemy trzymać w localstorage obiekty złożone)
- JSON parse parsuje odczytywany JSON string na obiekt JS
- Funkcja initializer sprawdza, czy obiekt o takim kluczu nie jest już obecny w localstorage – dzięki temu jeżeli coś tam jest zostawione po poprzednim mountowaniu komponentu to używamy tego
- Jeżeli nie, to funkcja initializer zwraca initialstate…
- useEffect gdy tylko zmieni się value albo key, bo takie są zależności, zapisuje aktualną wartość stanu do localstorage
- W JS nie ma multiple returns jak w Pythonie (tupla), ale znaną nam konwencją zwracamy tablicę z kilkoma elementami
Stan jako obiekt lub tablicę poznamy w następnych lekcjach.