Jak zamiast wielu useState używać useReducer?

React

readTime

4 min

Jak zamiast wielu useState używać useReducer?

Czy kiedykolwiek znalazłeś się w sytuacji, w której musiałeś użyć zbyt wielu hoków useState w jednym komponencie? Oto przykład tego, o czym mówię:

app.js

_16
import {useState} from "react";
_16
_16
export const App = ()=>{
_16
const [name,setName] = useState("");
_16
const [something, setSomething] = useState("");
_16
const [email, setEmail] = useState("");
_16
const [phone, setPhone] = useState("");
_16
const [courseStartDate, setCourseStartDate] = useState(null)
_16
const [courseEndDate, setCourseEndDate] = useState(null);
_16
return(
_16
<div>
_16
<input value={name} onChange={(e)=> setName(e.target.value)} />
_16
<p>{name} </p>
_16
</div>
_16
)
_16
}

Załóżmy, że powyższy komponent jest używany do aktualizacji danych użytkownika, który zapisał się na kurs. Zajmijmy się najpierw wszystkimi problemami związanymi z powyższym podejściem, a następnie omówimy rozwiązania i alternatywy w tym artykule

Jaki jest w tym problem?

Po pierwsze, nie ma żadnych zabezpieczeń. Na przykład, w tym podejściu nie ma sposobu, aby sprawdzić, czy courseEndDate powinien być dzień po courseStartDate. Nie ma również zabezpieczenia dla nazwy, która nie powinna zawierać żadnych liczb itp.

Oczywiście możemy stworzyć osobne handler'y dla wszystkich tych funkcjonalności, ale to sprawia, że kod jest zbyt długi i nieporęczny. Zawsze lepiej jest mieć je w jednym miejscu dla wszystkich tych zabezpieczeń.

app.js

_25
import {useState} from "react";
_25
_25
export const App = ()=>{
_25
const [name,setName] = useState("");
_25
const [something, setSomething] = useState("");
_25
const [email, setEmail] = useState("");
_25
const [phone, setPhone] = useState("");
_25
const [courseStartDate, setCourseStartDate] = useState(null)
_25
const [courseEndDate, setCourseEndDate] = useState(null);
_25
_25
const handleNameChange = (e)=>{
_25
const hasNumber = /\d$/.test(e.target.value);
_25
if (!hasNumber) setName(e.target.value);
_25
}
_25
_25
const handleEndDate = (value) =>{
_25
if(courseEndDate > courseStartDate) setCourseEndDate(value)
_25
}
_25
return(
_25
<div>
_25
<input value={name} onChange={handleNameChange} />
_25
<p>{name}</p>
_25
</div>
_25
)
_25
}

Jaka jest alternatywa?

Używając useReducer, moglibyśmy przekształcić powyższy kod w następujący sposób:

app.js

_31
import {useReducer} from "react";
_31
_31
export const App = ()=>{
_31
const [data, updateData] = useReducer(
_31
(prev,next) => {
_31
const updatedData = {...prev, ...next};
_31
_31
const hasNumber = /\d$/.test(updatedData.name);
_31
if (!hasNumber) updatedData.name = prev.name;
_31
_31
if (updatedData.courseEndDate > updatedData.courseStartDate)
_31
updatedData.courseEndDate = updateData.courseStartDate;
_31
_31
return updatedData;
_31
},
_31
{
_31
name: "",
_31
email:"",
_31
something:"",
_31
phone:"",
_31
courseStartDate:null,
_31
courseEndDate:null
_31
}
_31
);
_31
return(
_31
<div>
_31
<input value={data.name} onChange={(e)=> updateData({name:e.target.value})} />
_31
<p>{name}</p>
_31
</div>
_31
)
_31
}

Jak widać powyżej, wszystkie zabezpieczenia mamy w jednej funkcji i założę się, że dzięki takiemu podejściu kod będzie wyglądał jeszcze czyściej.

Dlaczego nie useState?

Ale czekaj, możesz również zastanawiać się, czy możesz zrobić to samo z samym useState bez użycia useReducer. Ale jest tu jeden haczyk. Z useState będziesz miał pojedynczy stan ze wszystkimi właściwościami, ale nie będziesz w stanie mieć wszystkich tych zabezpieczeń w jednym miejscu.

Jedno ważne ostrzeżenie

Jak zauważyłeś w powyższym kodzie, użyliśmy operatora spread(...), aby upewnić się, że nie zmutujemy obiektu. Musimy tutaj pamiętać o niezmienności (immutability), ponieważ jeśli zmutujemy, spowoduje to, że React nie wyrenderuje się zgodnie z oczekiwaniami.

Dzięki temu modelowi masz scentralizowane miejsce w kodzie, aby dodać zabezpieczenia w razie potrzeby. Wystarczy wywołać updateData() i to wszystko. Jak widzisz masz wiele możliwości konfiguracji w prosty sposób.

Wnioski

Nie oznacza to, że nigdy nie powinniśmy używać useState. Oba hooki są równie ważne i musimy podjąć właściwą decyzję, kiedy czego użyć. Zawsze należy pamiętać, że wartość stanu hooka useReducer musi być traktowana jako niezmienna. Zawsze możemy połączyć to z useContext, aby współdzielić stan między różnymi komponentami, co czyni to jeszcze potężniejszym. Mam nadzieję, że materiał był dla Ciebie przydatny i zapraszam do kolejnych ;)

źródło: https://javascript.plainenglish.io/stop-using-too-many-usestate-in-react-e613e021b33c

authorImg

Witek Pruchnicki

Z pasją dzielę się wiedzą o programowaniu i nie tylko na różne sposoby

Spis treści