Zduplikowany kod to zawsze miejsce, aby się zatrzymać i zastanowić, jak go ujednolicić.
Napotkałem ostatnio problem z duplikacją kodu w mojej aplikacji. Dwie różne trasy (route’y) prowadziły do dwóch różnych formularzy, które w praktyce robiły dokładnie to samo. Chodziło o formularze do tworzenia oraz edytowania jednej encji. Zacząłem się zastanawiać, czy faktycznie potrzebuję dwóch osobnych formularzy.
Identyfikacja problemu
Przy bliższym przyjrzeniu się zauważyłem, że:
- Oba formularze miały identyczne pola i walidacje.
- Różniły się jedynie sposobem pobierania początkowych wartości oraz akcją wykonywaną po kliknięciu przycisku „Zapisz” lub „Utwórz”.
- Każda zmiana w jednym formularzu musiała być powtarzana w drugim, co prowadziło do błędów i było trudne w utrzymaniu.
Początkowe przykłady formularzy (duplikacja)
Formularz do dodawania:
<form onSubmit={createEntity}>
<input name="name" placeholder="Nazwa" />
<input name="address" placeholder="Adres" />
<input name="nip" placeholder="NIP" />
<input name="regon" placeholder="Regon" />
<button type="submit">Utwórz</button>
</form>
Formularz do edycji:
<form onSubmit={editEntity}>
<input name="name" value={entity.name} />
<input name="address" value={entity.address} />
<input name="nip" value={entity.nip} />
<input name="regon" value={entity.regon} />
<button type="submit">Zapisz zmiany</button>
</form>
Jest tu widoczna wyraźna przestrzeń do optymalizacji.
Rozwiązanie
Postanowiłem stworzyć jeden dynamiczny formularz, który w zależności od parametru identyfikującego dane:
- jeśli parametr istnieje – formularz pobiera dane z API i wypełnia pola (edycja),
- jeśli nie – formularz pozostaje pusty (dodawanie).
Implementacja (przykład z SolidJS)
W moim przypadku używaliśmy solid-routera dla tras:
entity/:id/newentity/:id/edit
Pobranie parametru z URL:
const params = useParams();
Pobieranie danych, jeśli parametr istnieje:
const [data] = createResource(params.id, fetchDataById);
Ustawianie wartości początkowych formularza:
const [formState, setFormState] = createSignal({ name: "", address: "", nip: "", regon: "" });
createEffect(() => {
if (data()) setFormState({ ...data() });
});
Dynamiczna obsługa wysłania formularza:
async function handleSubmit(e) {
e.preventDefault();
const currentData = formState();
if (params.id) {
await updateData(params.id, currentData);
} else {
await createData(currentData);
}
}
Renderowanie dynamicznych elementów:
<h2>{params.id ? "Edytuj dane" : "Dodaj nowe dane"}</h2>
<button type="submit">{params.id ? "Zapisz zmiany" : "Utwórz"}</button>
Korzyści z zastosowania tego rozwiązania
- Usunięcie duplikacji kodu
- Uproszczenie testowania
- Łatwiejsze utrzymanie i rozwój aplikacji
- Spójny interfejs użytkownika, bardziej intuicyjny dla użytkownika końcowego
Podsumowanie
Dynamiczny formularz pozwala znacząco uprościć kod aplikacji, zmniejszyć ryzyko błędów i poprawić jej czytelność oraz komfort utrzymania. Szczególnie w aplikacjach SPA warto rozważyć wdrożenie tego podejścia, aby zapewnić jednolite doświadczenie użytkownikom oraz ułatwić rozwój projektu.
