Hopp til hovedinnhold

Det beste med kode: mye er mulig. Det verste med kode: mye er mulig. Her er 9 uvaner React-faggruppa stadig irriterer seg over i Pull Requester.

Brannfakler. Foto av Ta Dac Nguyen på Unsplash

Uvane 1: Ikke navngi useEffectene 🤐

Du er vel vant til å se useEffect-ene dine slik:

useEffect(() => {
  console.log("hei");

  throw new Error("En feil skjedde");
}, [])

Men når denne feiler, får vi en lite beskrivende feil i loggen:

Feilmeldingen i console-en sier det skjedde noe ved "_temp" på filplassering index.tsx.
Feilmeldingen i console-en sier det skjedde noe ved "_temp", hvor enn det er.

En rask løsning er å navngi useEffect-ene:

useEffect(function logConsole() {
  console.log("hei");

  throw new Error("En feil skjedde");
}, [])

Ved å legge til "function logConsole()" får vi en litt mer hjelpsom feilmelding:

Feilmeldingen i console-en sier det skjedde noe ved logConsole, som er funksjonen vi ga navn.
Nå sier feilmeldingen det skjedde en feil ved funksjonen "logConsole"

Uvane 2: Bruke arrow-functions 🏹

Når jeg leser nyheter, eller... skummer... så liker jeg å se det viktigste først. Det gjelder også når jeg leser kode. Om jeg bruker arrow-functions kan jeg ikke skrive slik:

// ❌ Feiler, ettersom funksjonen kalles før den er definert
someFunction();

const someFunction = () => {
  console.log("hei");
};

Dette gjør at jeg må bla gjennom mange hjelpefunksjoner før jeg kommer til det jeg egentlig bryr meg om.

Mens om du bruker en navngitt funksjon, som dette:

someFunction();

function someFunction() {
  console.log("hei");
};

... går det helt fint.

Årsaken er at funksjonen blir "hoisted". Det betyr at funksjonen blir definert før den blir kalt (selv om koden vi skrev er i en annen rekkefølge).

Før huset tar fyr, kan jeg gi deg litt mer nyanse: om ikke kutte arrow-functions helt ut, bør du ha et reflektert forhold til når du bruker arrow- functions og ikke. Du trenger ikke slutte med map- og reduce og resten av familien, da de defineres og kalles på samme sted, uten at du trenger å pakke vekk koden. Men kode du vil abstrahere kan godt eksistere som en vanlig funksjon.

Uvane 3: Refaktorere en komponent til sin egen fil bare fordi antall linjer har passert 3-sifret 📜

Jeg er ikke stor på haiku-dikt. Du vet, de svært korte diktene? Om jeg måtte bla en side for hver gang jeg skulle få ut en mening, tror jeg kodingen min hadde blitt ganske treg.

Om du definerer det viktigste først, kan du lese funksjonene som en artikkel. Uten at du må slå opp hvert ord i en annen artikkel. Filer gir en grense for hvor ting ikke henger sammen, men det trenger ikke å være på en regelsatt 100-linjers- grense (selv om jeg får vondt om jeg åpner noen på 500).

Så tillat deg selv å gå over. Når det gir mening.

Uvane 4: Abstrahere ut funksjoner tidlig 🌅

Hvordan kan du vite at mønsteret du abstraherer ut er det gjeldende, om du ikke har sett det nok ganger?

Bare et eksempel, hvor vi har putta all validering i én funksjon:

function validate(val) {
  if (required && !val) return "Påkrevd";
  if (type === "email" && !val.includes("@")) return "Ugyldig e-post";
  if (type === "password" && val.length < 8) return "For kort passord";
  return null;
}

Det kan fort bli veldig mye å ta hensyn til. Med enkeltfunksjoner er det lettere å vite hva som foregår også, selv om noe er duplisert:

function validateEmail(val) {
  if (!val) return "E-post er påkrevd";
  if (!val.includes("@")) return "Ugyldig e-post";
  return null;
}

function validatePassword(val) {
  if (!val) return "Passord er påkrevd";
  if (val.length < 8) return "Minst 8 tegn";
  return null;
}

Du må se en kodesnutt nok ganger til å vite hva som er felles før du gjør det til noe generelt. Ellers blir det grusomt kompliserte unntak i koden.

Uvane 5: Bruke default export 👯

Jeg vet vi går inn i en sesong fylt av overraskelser. Men, kall meg grinch om du vil, når det kommer til kode, liker jeg ikke overraskelser.

Si du har en HeaderComponent:

function HeaderComponent() {
  return ();
}

export default HeaderComponent;

... som du så importerer slik:

import HeaderComponent from './HeaderComponent';

Alt er nå vel og bra.

Men så finner du på å endre komponentnavn til MobileHeaderComponent. Du endrer filnavnet og navnet i komponenten. Men der du bruker komponenten, får du en mismatch i komponentnavn og filnavn:

import HeaderComponent from './MobileHeaderComponent';

Filnavn er endret, men siden en default export kan bli kalt hva som helst, har ikke navneendringen fulgt etter.

Jeg vet det fins referanse-funksjonalitet i IDE-en, men at jeg ikke kan globalt søke på filer fordi de kalles kanskje noe annet, er irriterende for meg.

Så vær så snill, gi meg en named export:

export function HeaderComponent() {
  return ();
}

// og i en annen komponent:
import { HeaderComponent } from "HeaderComponent";

Ellers endrer jeg i filen selv.

Uvane 6: Skrive egne memoiseringsfunksjoner 🧩

Jeg husker (pun intended) jeg brukte masse tid på å forstå hva useCallback, useMemo og den høyereordens-funksjonen memo gjorde. Funksjonene hjalp React å forstå når den ikke trengte å rendre - og å huske tunge operasjoner, så React ikke trengte å gjøre tunge kalkulasjoner på nytt, så appen ble raskere.

Det var vanskelig å wrappe hodet rundt disse funksjonene. Og når du først klarte det, var det vanskelig å bruke dem rett.

Nå med React Compiler klarer React å vite selv når noe arbeid trenger å huskes. Den klarer til og med optimalisere mer enn det du hadde klart selv.

Så om du ikke har lært deg memoiseringsfunksjonene, går det helt fint. Det er bare å schmække på React Compiler. Og om du har det i appen allerede, kan du glede deg til å fjerne masse overflødig kode - og gjøre appen litt mer leselig. Og kanskje raskere.

Også ser det jo kult ut når den gjør arbeidet:

Visning av komponent-tre med "memo" på memoiserte komponenter
Med React Compiler aktivert kan React skjønne hvilke komponenter som bør huskes verdier på - for raskere rendring og apper. Her fra Components-fanen i utviklerverktøyet.

Uvane 7: Optimalisere før du har det vondt 🤕

Selv om React ikke er det kjappeste JavaScript- biblioteket, er det veldig kjapt. I dag sliter ikke de fleste med trege enheter, men heller med tregt nett. Så det er mye du ikke trenger å optimalisere.

Optimalisering har en kostnad. Optimalisering kan gjøre koden mer komplisert, og overheaden den medbringer, kan faktisk gjøre koden tregere.

Et eksempel er å putte memoiseringsfunksjon på en enkel filtrering (et eksempel, for React Compiler tar seg av dette automatisk):

const filtered = React.useMemo(() => {
  return products.filter(p =>
    p.name.toLowerCase().includes(search.toLowerCase())
  );
}, [products, search]);

Om produktene ikke er i tusentalls, har det ikke noe å si å huske denne operasjonen.

Så vent til du tror optimaliseringen faktisk har en effekt, ikke bare fordi du tror du bør.

Uvane 8: Tillate ugyldig tilstand 🙅

Jeg tror ikke dette er kontroversiell brannfakkel, men det er fort gjort å tillate ugyldig tilstand.

Se eksempelet her, hvor vi har en handlekurv:

function ShoppingList() {
  const [items, setItems] = React.useState<string[]>([]);
  // 👇 Potensielt ugyldig:
  const [hasItems, setHasItems] = React.useState(false);

  function addItem() {
    const newItems = [...items, "Brød"];
    setItems(newItems);
    // 👇 Woops, glemte å oppdatere hasItems:
    // setHasItems(newItems.length > 0);
  }

  return (
    <div>
      <button onClick={addItem}>Legg til</button>
      {hasItems && <p>Du har varer i lista!</p>}
      <ul>
        {items.map((item, i) => <li key={i}>{item}</li>)}
      </ul>
    </div>
  );
}

Siden hasItems er en tilstand uavhengig av items, kan tilstandene settes ulikt. Det er fort gjort for utvikleren å glemme å endre.

Istedet kan vi la én tilstand bære på sannheten, og utlede resten:

function ShoppingList() {
  const [items, setItems] = React.useState<string[]>([]);

  function addItem() {
    setItems((prev) => [...prev, "Brød"]);
  }

  // 👇 hasItems er nå utledet av sannheten
  const hasItems = items.length > 0;

  return (
    <div>
      <button onClick={addItem}>Legg til</button>
      {hasItems && <p>Du har varer i lista!</p>}
      <ul>
        {items.map((item, i) => <li key={i}>{item}</li>)}
      </ul>
    </div>
  );
}

Nå er hasItems utledet av items, så det er mindre fare for feil.

Uvane 9: Bruke useEffect før du har vurdert andre tilnærminger 🤔

Om du allerede har tilstand for fornavn og etternavn, og vil vise fullt navn når de endrer seg – hva gjør du?

Jeg tror mange ville løst det slik:

function Profile() {
  const [firstName, setFirstName] = React.useState("");
  const [lastName, setLastName] = React.useState("");
  
  const [fullName, setFullName] = React.useState("");

  useEffect(function setName() {
    // 👇 Når firstName eller lastName endrer seg, oppdater fullName
    setFullName(`${firstName} ${lastName}`);
  }, [firstName, lastName]);

Mange tenker "når X endrer seg, og y skal følge, må jeg bruke useEffect". Men ofte trenger du ikke en useEffect og enda en tilstand. Du kan heller utlede en verdi fra eksisterende tilstand:

function Profile() {
  const [firstName, setFirstName] = React.useState("");
  const [lastName, setLastName] = React.useState("");
  // 🧹 Fjernet en tilstand og useEffect

  // 👇 Utleder verdien fra de andre tilstandene
  const fullName = `${firstName} ${lastName}`;

Å utlede verdier er også overførbart i andre mer kompliserte eksempler, som bruk med TanStack Query, som jeg har skrevet om før.

Du kan utlede verdier når du vil oppdatere React-tilstand. Så kan du bruke useEffect til å synkronisere en komponent med et eksternt system, som localStorage:

function ThemeToggle() {
  const [theme, setTheme] = useState("light");

  useEffect(() => {
    // 👇 når React-tilstand endres, oppdatere ekstern tilstand 
    window.localStorage.setItem("theme", theme);
  }, [theme]);

Så om du føler behovet for å strekke etter en useEffect, se først etter en annen tilnærming.

Avsluttende ord 🔚

Jeg håper du ble akkurat nok provosert til å reflektere over valgene du gjør når du skriver React-kode. Kanskje er du enig, og har øvd nikke-muskelen. Kanskje er du uenig, og gjør klar motsvaret. Uansett er jeg nysgjerrig på hvilke uvaner du vil rydde bort før 2026.

Liker du innlegget?

Del gjerne med kollegaer og venner