Rules Hub

Coding Rules Library

← Back to all rules
frontend ruleStack: react
reactclean-coderefactoringcomponentsreadability

Extract complex modal content into dedicated components

Move complex modal UI and its specific data transformation logic into separate components to improve readability and testability.

PR: Feat/FCK-1561 - Adding Invoice Table to Minside #3570Created: Dec 8, 2025

Bad example

Old codetsx
1const InvoiceList = ({ invoices }) => {
2 const [selected, setSelected] = useState<Invoice | null>(null);
3
4 // Helper defined inside parent component body
5 const getDetailRows = (inv: Invoice) => [
6 { label: 'Dato', value: inv.date },
7 { label: 'Beløp', value: inv.amount },
8 ];
9
10 return (
11 <div>
12 {invoices.map(inv => <div onClick={() => setSelected(inv)}>{inv.id}</div>)}
13
14 {/* Inline Modal logic mixed with parent logic */}
15 {selected && (
16 <Modal isOpen={!!selected} onClose={() => setSelected(null)}>
17 <div className="modal-header">
18 <h3>Faktura {selected.id}</h3>
19 <button onClick={() => setSelected(null)}>Close</button>
20 </div>
21 <div className="modal-body">
22 {getDetailRows(selected).map((row, idx) => (
23 <div key={idx} className="row">
24 <span>{row.label}</span>
25 <span>{row.value}</span>
26 </div>
27 ))}
28 </div>
29 </Modal>
30 )}
31 </div>
32 );
33};

Explanation (EN)

Defining the Modal UI and its helper functions (like `getDetailRows`) inside the parent component bloats the file. It mixes the list logic with the detail view logic, making the parent hard to read and the modal hard to test in isolation.

Objašnjenje (HR)

Definiranje UI-a modala i njegovih pomoćnih funkcija (poput `getDetailRows`) unutar roditeljske komponente nepotrebno povećava datoteku. Miješa logiku liste s logikom detaljnog prikaza, čineći roditelja teškim za čitanje, a modal teškim za izolirano testiranje.

Good example

New codetsx
1// InvoiceDetailModal.tsx
2const InvoiceDetailModal = ({ invoice, onClose }: { invoice: Invoice, onClose: () => void }) => {
3 // Helper logic is now encapsulated within the modal component
4 const detailRows = [
5 { label: 'Dato', value: invoice.date },
6 { label: 'Beløp', value: invoice.amount },
7 ];
8
9 return (
10 <Modal isOpen={true} onClose={onClose}>
11 <div className="modal-header">
12 <h3>Faktura {invoice.id}</h3>
13 <button onClick={onClose}>Close</button>
14 </div>
15 <div className="modal-body">
16 {detailRows.map((row, idx) => (
17 <div key={idx} className="row">
18 <span>{row.label}</span>
19 <span>{row.value}</span>
20 </div>
21 ))}
22 </div>
23 </Modal>
24 );
25};
26
27// InvoiceList.tsx
28const InvoiceList = ({ invoices }) => {
29 const [selected, setSelected] = useState<Invoice | null>(null);
30
31 return (
32 <div>
33 {invoices.map(inv => <div onClick={() => setSelected(inv)}>{inv.id}</div>)}
34
35 {/* Clean and semantic usage */}
36 {selected && (
37 <InvoiceDetailModal
38 invoice={selected}
39 onClose={() => setSelected(null)}
40 />
41 )}
42 </div>
43 );
44};

Explanation (EN)

The modal logic is extracted into a dedicated `InvoiceDetailModal` component. The parent component remains focused on the list logic, and the modal handles its own data formatting and rendering. This improves readability and allows the modal to be tested separately.

Objašnjenje (HR)

Logika modala izdvojena je u zasebnu komponentu `InvoiceDetailModal`. Roditeljska komponenta ostaje fokusirana na logiku liste, dok modal samostalno upravlja formatiranjem i prikazom svojih podataka. Ovo poboljšava čitljivost i omogućuje zasebno testiranje modala.