React-mentale modellen kunnen React eenvoudig laten aanvoelen: leer de kernideeën achter componenten, rendering, state en effecten en pas ze toe om snel UI te bouwen via chat.

React kan in het begin frustrerend zijn omdat je de UI ziet veranderen, maar niet altijd kunt verklaren waarom het veranderde. Je klikt op een knop, iets update, en een ander deel van de pagina verrast je. Dat is meestal niet “React is raar.” Het is “mijn beeld van wat React doet is wazig.”
Een mentaal model is het eenvoudige verhaaltje dat je jezelf vertelt over hoe iets werkt. Als dat verhaaltje niet klopt, neem je zelfverzekerde beslissingen die tot verwarrende resultaten leiden. Denk aan een thermostaat: een slecht model is “ik zet 22°C en de kamer wordt meteen 22°C.” Een beter model is “ik zet een doel en de verwarming schakelt aan en uit over tijd om dat doel te bereiken.” Met het betere verhaal stopt het gedrag met willekeurig aan te voelen.
React werkt hetzelfde. Zodra je een paar duidelijke ideeën aanhoudt, wordt React voorspelbaar: je kunt naar de huidige data kijken en betrouwbaar raden wat op het scherm staat.
Dan Abramov hielp dit ‘maak het voorspelbaar’-denkpatroon populair te maken. Het doel is niet regels uit je hoofd leren. Het is een klein aantal waarheden in je hoofd houden zodat je kunt debuggen door te redeneren, niet door trial-and-error.
Houd deze ideeën in gedachten:
Houd die vast en React stopt met aanvoelen als magie. Het wordt een systeem waarop je kunt vertrouwen.
React wordt makkelijker als je stopt met denken in “schermen” en begint te denken in kleine onderdelen. Een component is een herbruikbare UI-eenheid. Hij krijgt inputs en retourneert een beschrijving van hoe de UI eruit moet zien voor die inputs.
Het helpt om een component te behandelen als een pure beschrijving: “gegeven deze data, toon dit.” Die beschrijving kan op veel plaatsen gebruikt worden omdat hij niet afhangt van waar hij leeft.
Props zijn de inputs. Ze komen van een oudercomponent. Props zijn niet “eigendom” van de component en iets wat de component stilletjes zou moeten veranderen. Als een knop label="Save" krijgt, is de taak van de knop om dat label te renderen, niet om te besluiten dat het anders moet zijn.
State is eigendom-gegevens. Het is wat de component zich herinnert over de tijd. State verandert wanneer een gebruiker interacteert, wanneer een request klaar is, of wanneer jij beslist dat iets anders moet zijn. In tegenstelling tot props, hoort state bij die component (of bij welke component je ook kiest om het te bezitten).
De eenvoudige versie van het sleutelidee: UI is een functie van state. Als state zegt “loading”, toon een spinner. Als state zegt “error”, toon een bericht. Als state zegt “items = 3”, render drie rijen. Jouw taak is om de UI te laten lezen uit state, niet te laten afdrijven naar verborgen variabelen.
Een snelle manier om de concepten te scheiden is:
SearchBox, ProfileCard, CheckoutForm)name, price, disabled)isOpen, query, selectedId)Voorbeeld: een modal. De ouder kan title en onClose als props doorgeven. De modal kan isAnimating als state hebben.
Zelfs als je UI genereert via chat (bijvoorbeeld op Koder.ai), is deze scheiding nog steeds de snelste manier om sane te blijven: beslis eerst wat props en wat state is, en laat dan de UI volgen.
Een nuttige manier om React in je hoofd te houden (erg in de geest van Dan Abramov) is: rendering is een berekening, geen schilderklus. React voert je componentfuncties uit om uit te rekenen hoe de UI eruit moet zien voor de huidige props en state. De output is een UI-beschrijving, geen pixels.
Een re-render betekent gewoon dat React die berekening opnieuw uitvoert. Het betekent niet “de hele pagina wordt opnieuw getekend.” React vergelijkt het nieuwe resultaat met het vorige en past de kleinste set wijzigingen toe op de echte DOM. Veel componenten kunnen re-renderen terwijl slechts een paar DOM-nodes daadwerkelijk worden bijgewerkt.
De meeste re-renders gebeuren om een paar eenvoudige redenen: de state van een component veranderde, zijn props veranderden, of een ouder her-renderde en React vroeg het kind om weer te renderen. Dat laatste verrast mensen, maar het is meestal prima. Als je render behandelt als “goedkoop en saai”, blijft je app makkelijker te redeneren.
De vuistregel die dit schoon houdt: maak render puur. Gegeven dezelfde inputs (props + state) zou je component dezelfde UI-beschrijving moeten teruggeven. Houd verrassingen uit render.
Concreet voorbeeld: als je een ID genereert met Math.random() in render, verandert die bij een re-render en verliest een checkbox opeens focus of remount een lijstitem. Maak de ID één keer aan (state, memo, of buiten de component) en render wordt stabiel.
Als je één zin onthoudt: een re-render betekent “herbereken wat de UI moet zijn,” niet “bouw alles opnieuw op.”
Nog een behulpzaam model: state-updates zijn verzoeken, geen onmiddellijke toewijzingen. Wanneer je een setter aanroept zoals setCount(count + 1), vraag je React om een render te plannen met een nieuwe waarde. Als je direct daarna state leest, zie je misschien nog de oude waarde omdat React nog niet gerenderd heeft.
Daarom zijn “kleine en voorspelbare” updates belangrijk. Geef de voorkeur aan het beschrijven van de verandering in plaats van het grijpen van wat jij denkt dat de huidige waarde is. Wanneer de volgende waarde afhangt van de vorige, gebruik de updater-vorm: setCount(c => c + 1). Dat past bij hoe React werkt: meerdere updates kunnen in de wachtrij staan en vervolgens in volgorde toegepast worden.
Immutability is de andere helft van het plaatje. Verander geen objecten en arrays ter plekke. Maak een nieuwe aan met de wijziging. React kan dan zien “deze waarde is nieuw,” en jouw brein kan traceren wat er veranderd is.
Voorbeeld: togglen van een todo-item. De veilige aanpak is om een nieuwe array en een nieuw todo-object voor dat ene item te maken. De riskante aanpak is todo.done = !todo.done in de bestaande array te flippen.
Houd ook state minimaal. Een veelgemaakte val is waarden opslaan die je kunt berekenen. Als je al items en filter hebt, sla dan niet filteredItems in state op. Bereken het tijdens render. Minder state-variabelen betekent minder manieren waarop waarden uit sync kunnen raken.
Een simpele test voor wat er in state hoort:
Als je UI via chat bouwt (inclusief op Koder.ai), vraag dan om veranderingen als kleine patches: “Voeg één boolean-flag toe” of “Werk deze lijst immutably bij.” Kleine, expliciete veranderingen houden de generator en je React-code in lijn.
Rendering beschrijft UI. Effects synchroniseren met de buitenwereld. “Buiten” betekent dingen die React niet controleert: netwerkcalls, timers, browser API's en soms imperatief DOM-werk.
Als iets berekend kan worden uit props en state, hoort het meestal niet in een effect. Het in een effect plaatsen voegt een tweede stap toe (render, run effect, set state, render opnieuw). Die extra stap is waar flikkeringen, loops en “waarom is dit verouderd?”-bugs vandaan komen.
Een veelvoorkomende verwarring: je hebt firstName en lastName, en je slaat fullName in state op via een effect. Maar fullName is geen side effect. Het is afgeleide data. Bereken het tijdens render en het zal altijd overeenkomen.
Als gewoonte: leid UI-waarden af tijdens render (of met useMemo als iets echt duur is), en gebruik effecten voor “doe iets”-werk, niet voor “bereken iets”-werk.
Behandel de dependency-array als: “Wanneer deze waarden veranderen, resynchroniseer met de buitenwereld.” Het is geen performance-truc en geen plek om waarschuwingen te onderdrukken.
Voorbeeld: als je gebruikersdetails fetched wanneer userId verandert, hoort userId in de dependency-array omdat het de sync zou moeten triggeren. Als het effect ook token gebruikt, voeg dat dan toe, anders fetch je misschien met een oude token.
Een goede vuistregel: als het verwijderen van een effect alleen de UI fout zou maken, was het waarschijnlijk geen echt effect. Als het verwijderen een timer stopt, een subscription annuleert of een fetch overslaat, dan was het waarschijnlijk wel een effect.
Een van de meest nuttige mentale modellen is simpel: data gaat naar beneden door de boom, en gebruikersacties gaan omhoog.
Een ouder geeft waarden aan kinderen. Kinderen zouden niet stiekem dezelfde waarde op twee plekken moeten “bezitten.” Ze vragen veranderingen aan door een functie aan te roepen, en de ouder beslist wat de nieuwe waarde is.
Als twee delen van de UI het eens moeten zijn, kies dan één plek om de waarde op te slaan en geef die omlaag. Dit is “lifting state.” Het kan voelen als extra leidingen, maar het voorkomt een erger probleem: twee states die uit elkaar drijven en je dwingen hacks toe te voegen om ze in sync te houden.
Voorbeeld: een zoekbalk en een resultatenlijst. Als de input zijn eigen query opslaat en de lijst ook zijn eigen query heeft, zie je uiteindelijk “input toont X maar lijst gebruikt Y.” De oplossing is query in één ouder te houden, naar beiden door te geven en een onChangeQuery(newValue) handler terug naar de input te geven.
Lifting state is niet altijd de oplossing. Als een waarde alleen binnen één component van belang is, houd hem daar. State dicht bij waar het gebruikt wordt houden maakt code meestal makkelijker leesbaar.
Een praktische grens:
Als je twijfelt of je moet liften, kijk dan naar signalen zoals: twee componenten tonen dezelfde waarde op verschillende manieren; een actie op één plek moet iets ver weg updaten; je kopieert props in state “voor het geval”; of je voegt effecten toe alleen om twee waarden gelijk te houden.
Dit model helpt ook wanneer je via chattools zoals Koder.ai bouwt: vraag een enkele eigenaar voor elk stuk gedeelde state en genereer handlers die omhoog stromen.
Kies een feature klein genoeg om in je hoofd te houden. Een goede is een doorzoekbare lijst waar je op een item kunt klikken om details in een modal te zien.
Begin met het schetsen van de UI-delen en de events die kunnen gebeuren. Denk nog niet aan code. Denk aan wat de gebruiker kan doen en wat hij kan zien: er is een zoekinput, een lijst, een geselecteerde rij markering en een modal. De events zijn typen in de zoekbalk, op een item klikken, de modal openen en de modal sluiten.
Teken nu “de state.” Schrijf de paar waarden op die bewaard moeten worden en beslis wie ze bezit. Een eenvoudige regel werkt goed: de dichtsbijzijnde gemeenschappelijke ouder van alle plekken die een waarde nodig hebben, zou die moeten bezitten.
Voor deze feature kan opgeslagen state klein zijn: query (string), selectedId (id of null) en isModalOpen (boolean). De lijst leest query en rendert items. De modal leest selectedId om details te tonen. Als zowel lijst als modal selectedId nodig hebben, houd het in de ouder, niet in beiden.
Scheid daarna afgeleide data van opgeslagen data. De gefilterde lijst is afgeleid: filteredItems = items.filter(...). Sla die niet in state op omdat je hem altijd kunt herberekenen uit items en query. Het opslaan van afgeleide data is hoe waarden uit sync raken.
Stel jezelf dan de vraag: hebben we een effect nodig? Als items al in geheugen zijn, nee. Als typen in de zoekbalk resultaten zou moeten fetchen, ja. Als het sluiten van de modal iets moet opslaan, ja. Effecten zijn voor synchronisatie (fetch, save, subscribe), niet voor basis UI-wiring.
Tot slot, test de flow met een paar randgevallen:
selectedId nog valide?Als je die op papier kunt beantwoorden, is de React-code meestal eenvoudig.
De meeste React-verwarring gaat niet over syntax. Het gebeurt wanneer je code stopt overeen te komen met het eenvoudige verhaal in je hoofd.
Afgeleide state opslaan. Je bewaart fullName in state terwijl het gewoon firstName + lastName is. Het werkt totdat één veld verandert en het andere niet, en de UI toont een verouderde waarde.
Effect-loops. Een effect fetched data, zet state en de dependency-list laat het opnieuw draaien. Het symptoom is herhaalde requests, schokkerige UI of state die nooit stabiliseert.
Stale closures. Een click-handler leest een oude waarde (zoals een verouderde teller of filter). Het symptoom is “ik klikte, maar het gebruikte gisteren’s waarde.”
Global state overal. Alles in een globale store stoppen maakt het moeilijk te zien wat wat bezit. Het symptoom is dat je iets verandert en drie schermen reageren op verrassende manieren.
Gemuteerde geneste objecten. Je werkt een object of array ter plaatse bij en vraagt je af waarom de UI niet updatet. Het symptoom is “de data veranderde, maar er renderde niets.”
Een concreet voorbeeld: een “search-and-sort” paneel voor een lijst. Als je filteredItems in state bewaart, kan het uit sync raken met items wanneer nieuwe data binnenkomt. Houd in plaats daarvan de inputs (search text, sort choice) in state en bereken de gefilterde lijst tijdens render.
Met effecten: houd ze voor synchronisatie met de buitenwereld (fetching, subscriptions, timers). Als een effect basis UI-werk doet, hoort het vaak in render of een event handler.
Wanneer je code genereert of bewerkt via chat, verschijnen deze fouten sneller omdat veranderingen in grote brokken kunnen komen. Een goede gewoonte is requests te kaderen in termen van eigenaarschap: “Wat is de bron van waarheid voor deze waarde?” en “Kunnen we dit berekenen in plaats van het op te slaan?”
Als je UI onvoorspelbaar wordt, is het zelden “te veel React.” Het is meestal te veel state, op de verkeerde plekken, die taken doet die het niet zou moeten doen.
Voordat je nog een useState toevoegt, pauzeer en vraag:
Klein voorbeeld: zoekbox, filter-dropdown, lijst. Als je zowel query als filteredItems in state bewaart, heb je nu twee bronnen van waarheid. Houd in plaats daarvan query en filter in state en leid filteredItems tijdens render af uit de volledige lijst.
Dit is belangrijk wanneer je snel bouwt via chattools. Snelheid is geweldig, maar blijf vragen: “Hebben we state toegevoegd, of hebben we per ongeluk een afgeleide waarde toegevoegd?” Als het afgeleid is, verwijder die state en bereken het.
Een goed beginnend model is: UI = f(state, props). Je componenten ‘bewerken’ de DOM niet; ze beschrijven wat er op het scherm moet staan voor de huidige data. Als het scherm verkeerd is, inspecteer dan de state/props die het voortbrachten, niet de DOM.
Props zijn inputs van een ouder; je component moet ze als alleen-lezen behandelen. State is geheugen dat eigendom is van een component (of van de component die jij kiest als eigenaar). Als een waarde gedeeld moet worden, lift je die omhoog en geef je hem als props door.
Een re-render betekent dat React je componentfunctie opnieuw uitvoert om de volgende UI-beschrijving te berekenen. Het betekent niet automatisch dat de hele pagina opnieuw wordt geschilderd. React werkt daarna de echte DOM bij met de kleinste benodigde wijzigingen.
Omdat state-updates gepland zijn, geen directe toewijzingen. Als de volgende waarde afhangt van de vorige, gebruik dan de updater-vorm zodat je niet afhankelijk bent van een mogelijk verouderde waarde:
setCount(c => c + 1)Dit blijft correct zelfs als meerdere updates in de wachtrij staan.
Vermijd het opslaan van alles wat je kunt berekenen uit bestaande inputs. Sla de inputs op en leid de rest af tijdens render.
Voorbeelden:
items, filtervisibleItems = items.filter(...)Dit voorkomt dat waarden uit sync raken.
Gebruik effecten om te synchroniseren met dingen die React niet beheert: fetching, subscriptions, timers, browser API's of imperatief DOM-werk.
Gebruik geen effect alleen om UI-waarden te berekenen uit state—bereken die tijdens render (of met useMemo als het duur is).
Behandel dependencies als een triggerlijst: “wanneer deze waarden veranderen, synchroniseer opnieuw.” Neem elke reactieve waarde op die het effect leest.
Laat je iets weg, dan riskeer je verouderde data (zoals een oude userId of token). Voeg je de verkeerde dingen toe, dan kun je loops creëren—vaak een teken dat het werk in events of render hoort.
Als twee delen van de UI altijd overeen moeten zijn, zet de state in hun dichtstbijzijnde gemeenschappelijke ouder, geef de waarde naar beneden en geef callbacks omhoog.
Een snelle test: als je dezelfde waarde in twee componenten dupliceert en effecten schrijft om ze “in sync te houden”, heeft die state waarschijnlijk één eigenaar nodig.
Het gebeurt vaak als een handler een oude waarde ‘captured’ heeft uit een vorige render. Veelvoorkomende fixes:
setX(prev => ...)Als een klik “gisteren’s waarde” gebruikt, verdenk een stale closure.
Begin met een klein plan: componenten, state-eigenaren en events. Genereer dan code als kleine patches (één stateveld toevoegen, één handler toevoegen, één waarde afleiden) in plaats van grote herschrijvingen.
Als je een chat-bouwer zoals Koder.ai gebruikt, vraag om:
Dat houdt de gegenereerde code in lijn met React’s mentale model.