Tailwind CSS Academy · Oppitunti

Ehdolliset luokat Reactissa

Käyttäkää clsx:ää tai tailwind-mergeä Tailwind-luokkien ehdolliseen käyttöönottoon ja turvalliseen yhdistämiseen komponenttien propsien ja tilan perusteella ilman luokkaristiriitoja.

Oppitunti 2/413 vaihetta

Ehdolliset luokat Reactissa on ilmainen Tailwind CSS Academy-oppitunti CoddyKitissä. Tämä on oppitunti 2/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu Tailwind CSS Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Tailwind CSS Academy-kurssilla on yhteensä 4 oppituntia.

Merkkijonojen yhdistämisen ongelma

Tailwind-luokkien käyttäminen ehdollisesti Reactissa on aluksi suoraviivaista, mutta naiivi merkkijonojen yhdistäminen johtaa nopeasti virheisiin. Merkkijonojen yhdistäminen mallimerkkijonoilla voi vahingossa lisätä undefined- tai false-arvon className-määritykseen, mikä johtaa virheellisiin luokkien nimiin DOMissa. Vielä vakavampi ongelma on, etteivät ristiriitaiset Tailwind-apuohjelmat — kuten text-blue-500 ja text-red-500 — kumoa toisiaan. Voittajan ratkaisee tyylitiedoston järjestys, ei className-merkkijonon järjestys.

// Problematic — may include 'false' or 'undefined' in class list
function Button({ disabled, primary }) {
  return (
    <button
      className={'px-4 py-2 rounded ' +
        primary && 'bg-blue-500 text-white' +  // bug: && short-circuit
        disabled && 'opacity-50'               // 'false' in string
      }
    >
      Click me
    </button>
  );
}

clsx:n käyttäminen ehdollisiin luokkiin

clsx on pieni apuohjelma, joka muodostaa className-merkkijonoja turvallisesti ehtojen, olioiden ja taulukoiden perusteella. Se suodattaa pois epätotuusarvot, kuten false-, null- ja undefined-arvot, joten DOMiin päätyvät aina siistit luokkien nimet. Voitte välittää sille merkkijonoja, boolean-arvoja sisältäviä olioita tai näiden taulukoita. Asentakaa se komennolla npm install clsx ja tuokaa se käyttöön kaikkialla, missä tarvitsette ehdollista luokkalogiikkaa.

import clsx from 'clsx';

function Button({ disabled, primary, className }) {
  return (
    <button
      className={clsx(
        'px-4 py-2 rounded font-medium transition-colors',
        primary && 'bg-blue-600 text-white hover:bg-blue-700',
        !primary && 'bg-gray-100 text-gray-900 hover:bg-gray-200',
        disabled && 'opacity-50 cursor-not-allowed',
        className  // allow caller to pass extra classes
      )}
      disabled={disabled}
    >
      Click me
    </button>
  );
}

clsx-oliosyntaksi

clsx hyväksyy oliosyntaksin, jossa avaimet ovat luokkien nimiä ja arvot boolean-ehtoja. Tämä on erityisen selkeää, kun useita ehdollisia luokkia ryhmitellään tarkoituksen mukaan. Voitte yhdistää olio- ja merkkijonoargumentteja samassa clsx-kutsussa, jolloin ehdottomat perusluokat on helppo erottaa ehdollisista varianttiluokista.

import clsx from 'clsx';

function Alert({ type }) {
  return (
    <div
      className={clsx(
        // Unconditional base classes
        'rounded-lg border p-4 flex items-start gap-3',
        // Conditional classes via object syntax
        {
          'bg-red-50 border-red-200 text-red-800': type === 'error',
          'bg-yellow-50 border-yellow-200 text-yellow-800': type === 'warning',
          'bg-green-50 border-green-200 text-green-800': type === 'success',
          'bg-blue-50 border-blue-200 text-blue-800': type === 'info'
        }
      )}
    >
      {/* alert content */}
    </div>
  );
}

Luokkien välinen ristiriita

Vaikka clsx tuottaa siistejä luokkien merkkijonoja, Tailwind-luokkien ristiriidat ovat edelleen ongelma. Jos isäelementti käyttää luokkaa text-blue-500 ja lapsielementti tai ylikirjoitus luokkaa text-red-500, molemmat luokat näkyvät DOMissa. CSS:n kaskadijärjestys (ei DOM-järjestys) ratkaisee voittajan — voittaja on se, jonka Tailwind on muodostanut tyylitiedostoon viimeisenä. Tämä tekee ylikirjoitusten toiminnasta arvaamatonta etenkin silloin, kun kutsuja välittää className-proppeja.

// Class conflict example
function Badge({ className }) {
  return (
    <span className={clsx('bg-blue-500 text-white px-2 py-0.5 rounded', className)}>
      Tag
    </span>
  );
}

// Caller tries to override background
<Badge className="bg-red-500" />
// Result: both bg-blue-500 AND bg-red-500 in the DOM
// Which one wins depends on Tailwind's stylesheet order, not your intent

tailwind-merge ratkaisee ristiriidat

tailwind-merge (twMerge) tuntee Tailwindin apuohjelmaryhmät ja varmistaa, että kunkin ristiriitaisen ominaisuuden viimeinen arvo voittaa. Se ymmärtää, että bg-blue-500 ja bg-red-500 määrittävät molemmat ominaisuuden background-color, joten se säilyttää niistä vain viimeisen. Asentakaa se komennolla npm install tailwind-merge ja käärikää clsx-kutsunne twMerge-funktion sisään, jotta ylikirjoitukset toimivat ennakoitavasti.

import { twMerge } from 'tailwind-merge';
import clsx from 'clsx';

function Badge({ className }) {
  return (
    <span
      className={twMerge('bg-blue-500 text-white px-2 py-0.5 rounded', className)}
    >
      Tag
    </span>
  );
}

// Caller override now works correctly!
<Badge className="bg-red-500" />
// Result: only bg-red-500 (bg-blue-500 is removed by twMerge)

cn()-apuohjelmamalli

Useimmissa Next.js- ja React-projekteissa näette cn()-nimisen apufunktion, joka yhdistää clsx:n ja tailwind-merge:n yhdeksi käteväksi kutsuksi. Tämä malli on niin yleinen, että shadcn/ui:n create-next-app sisältää sen oletusarvoisesti. Määritelkää se kerran apuohjelmatiedostossa ja tuokaa se käyttöön kaikkialla, missä tarvitsette ehdollisten ja ristiriidattomien luokkien käsittelyä.

// lib/utils.ts
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

// Usage in any component
import { cn } from '@/lib/utils';

function Card({ className, children }) {
  return (
    <div className={cn(
      'rounded-xl border bg-white shadow-sm p-6',
      className
    )}>
      {children}
    </div>
  );
}

Tilaan perustuvat ehdolliset luokat

cn()-apuohjelma on hyödyllinen erityisesti silloin, kun komponentin tila ohjaa visuaalisia muutoksia. Vaihtokytkin, aktiivinen navigointikohde tai kelpoisuusvirheen sisältävä lomakekenttä tarvitsee luokkia, jotka muuttuvat JavaScript-tilan perusteella. cn-funktion käyttäminen olio-syntaksilla tekee tilan ja tyylien välisestä suhteesta heti selkeän — komponentin lukija näkee suoraan, mitkä luokat otetaan käyttöön missäkin tilanteessa.

import { cn } from '@/lib/utils';

function NavItem({ href, active, children }) {
  return (
    <a
      href={href}
      className={cn(
        'flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium',
        'transition-colors duration-150',
        {
          'bg-blue-50 text-blue-700': active,
          'text-gray-600 hover:bg-gray-100 hover:text-gray-900': !active
        }
      )}
    >
      {children}
    </a>
  );
}

className-proppien turvallinen välittäminen

Uudelleenkäytettävien komponenttien keskeinen suunnittelupäätös on className-propin käsittelytapa. twMerge-funktion avulla kutsujan välittämät luokat voivat ylikirjoittaa oletukset asianmukaisesti. Joitakin luokkia ei kuitenkaan pitäisi voida ylikirjoittaa koskaan — esimerkiksi komponentin asettelun määrittäviä rakenteellisia luokkia. Yksi tapa on lisätä rakenteelliset luokat ilman yhdistämistä ja yhdistää kutsujan syötteen kanssa vain esitystapaan liittyvät luokat.

import { cn } from '@/lib/utils';

function Input({ className, hasError, ...props }) {
  return (
    <input
      className={cn(
        // Base structural: never overridden
        'block w-full rounded-lg border px-3 py-2 text-sm',
        'placeholder:text-gray-400 focus:outline-none focus:ring-2',
        // Conditional state classes
        hasError
          ? 'border-red-300 focus:ring-red-500'
          : 'border-gray-300 focus:ring-blue-500',
        // Caller overrides presentational classes
        className
      )}
      {...props}
    />
  );
}

Dynaamisten luokkien nimien välttäminen

Tailwindin JIT-moottori tunnistaa luokkien nimet etsimällä lähdetiedostoista kokonaisia merkkijonoja. Älkää koskaan muodostako luokkien nimiä dynaamisesti yhdistämällä osittaisia merkkijonoja, kuten 'text-' + color + '-500'. JIT-skanneri ei tunnista näitä, ja luokat poistetaan tuotantopaketista. Käyttäkää aina kokonaisia luokkien nimien merkkijonoja, vaikka tämä tarkoittaisi pidempää ehtolauseketta.

// WRONG — JIT cannot detect these dynamic class names
const colors = { info: 'blue', error: 'red' };
<div className={'text-' + colors[type] + '-500'} />

// CORRECT — full class names that JIT can detect
const colorMap = {
  info: 'text-blue-500 bg-blue-50',
  error: 'text-red-500 bg-red-50',
  success: 'text-green-500 bg-green-50'
};
<div className={cn('rounded p-3', colorMap[type])} />

Luokkalaskennan memoizointi

Kun komponentti renderöityy usein ja sen className-laskenta on monimutkaista, harkitkaa tuloksen memoisointia useMemo-hookilla. clsx- ja twMerge-operaatiot ovat nopeita, mutta satoja kertoja sekunnissa renderöityvissä komponenteissa, kuten virtuaalisen listan kohteissa, pienetkin säästöt kertyvät. Yleisemmin luokkalaskennan siirtäminen JSX:n ulkopuoliseen muuttujaan parantaa luettavuutta suorituskyvystä riippumatta.

import { useMemo } from 'react';
import { cn } from '@/lib/utils';

function ListItem({ selected, variant, className }) {
  const itemClasses = useMemo(() => cn(
    'flex items-center gap-3 px-4 py-3 cursor-pointer',
    'border-b border-gray-100 transition-colors',
    {
      'bg-blue-50 border-l-2 border-l-blue-500': selected,
      'hover:bg-gray-50': !selected,
      'opacity-50 pointer-events-none': variant === 'disabled'
    },
    className
  ), [selected, variant, className]);

  return <div className={itemClasses}>{/* content */}</div>;
}

Ehdollisen luokkalogiikan testaaminen

Kirjoittakaa yksikkötestit komponenteille, joissa on monimutkaista ehdollista luokkalogiikkaa, jotta regressiot voidaan estää. React Testing Libraryn avulla voitte tarkistaa, ovatko tietyt Tailwind-luokat käytössä prop-arvojen perusteella. Tämä on erityisen tärkeää saavutettavuuden kannalta olennaisille luokille — sen testaaminen, että käytöstä poistetulla painikkeella on cursor-not-allowed- ja opacity-50-luokat, antaa varmuuden käyttöliittymän vihjeiden oikeellisuudesta.

// Button.test.tsx
import { render, screen } from '@testing-library/react';
import { Button } from './Button';

test('disabled button has correct classes', () => {
  const { container } = render(
    <Button disabled>Submit</Button>
  );
  const btn = container.firstChild;
  expect(btn.className).toContain('opacity-50');
  expect(btn.className).toContain('cursor-not-allowed');
});

test('primary variant applies correct colors', () => {
  const { container } = render(
    <Button primary>Submit</Button>
  );
  expect(container.firstChild.className).toContain('bg-blue-600');
});

Pikatarkistus

Testatkaa tämän oppitunnin Tailwind CSS Mastery -aiheiden ymmärtämistänne.

Oppitunnin yhteenveto

Tässä oppitunnissa opitte, että clsx muodostaa ehdolliset luokkien merkkijonot turvallisesti suodattamalla epätotuusarvot, tailwind-merge ratkaisee ristiriitaiset Tailwind-apuohjelmat niin, että viimeisenä käytetty voittaa, ja cn()-apuohjelma yhdistää nämä molemmat yhdeksi kutsuksi, jota käytetään kaikkialla React-projektissanne. Seuraavaksi tutustumme komponenttivariantteihin ja CVA:n tyypitettyihin variantti-rajapintoihin.

Aloita maksutta

Opi HTML tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
30
Oppitunnit
120

Usein kysytyt kysymykset

Onko oppitunti ”Ehdolliset luokat Reactissa” ilmainen?

Kyllä – oppitunnin ”Ehdolliset luokat Reactissa” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko Tailwind CSS Academy-kurssin, päivitä CoddyKit PROhon. Tailwind CSS Academy-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”Ehdolliset luokat Reactissa”?

Käyttäkää clsx:ää tai tailwind-mergeä Tailwind-luokkien ehdolliseen käyttöönottoon ja turvalliseen yhdistämiseen komponenttien propsien ja tilan perusteella ilman luokkaristiriitoja. Harjoittelet Tailwind CSS Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni Tailwind CSS Academy-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin Tailwind CSS Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 2/4.

Kuinka kauan ”Ehdolliset luokat Reactissa”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä Tailwind CSS Academy-oppitunnilla?

Kyllä. Jokainen Tailwind CSS Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. Tailwindin käyttöönotto Next.js:ssä
  2. Ehdolliset luokat Reactissa
  3. Komponenttivariantit CVA:lla
  4. Luokkaristiriitojen välttäminen tailwind-merge-kirjastolla
← Takaisin: Tailwind CSS Academy