clsx va classnames — dinamik class boshqarish

~10 daq · React · Stilizatsiya

Muammodan boshlaylik

React da komponentga bir nechta shartli klass qo'shish kerak bo'lsa, ko'pchilik shunday yozadi:

<div className={
  'card' +
  (isActive ? ' card--active' : '') +
  (isDisabled ? ' card--disabled' : '') +
  (size === 'lg' ? ' card--large' : '') +
  (hasError ? ' card--error' : '')
}>

Yoki template literal bilan:

<div className={`card ${isActive ? 'card--active' : ''} ${isDisabled ? 'card--disabled' : ''} ${hasError ? 'card--error' : ''}`}>

Ikkalasi ham ishlaydi. Lekin o'qish qiyin, xato qilish oson — shart bajarilmasa bo'sh string qoladi va className da ortiqcha bo'shliq hosil bo'ladi: "card card--active".

classnames va clsx bu muammoni hal qiladi.


classnames va clsx — farqi nima?

Ikkalasi bir xil vazifani bajaradi. Farq faqat hajmda:

classnames  →  ~1.1KB  (asl kutubxona, 2013 yildan beri)
clsx        →  ~0.3KB  (classnames ning engillashtirilgan versiyasi)

clsx — classnames ning to'liq o'rnini bosadi, lekin 4 marta kichik. Zamonaviy loyihalarda clsx ishlatiladi. classnames eski loyihalarda ko'rasiz — API bir xil.

npm install clsx
# yoki
npm install classnames

clsx — barcha sintaksis shakllari

String lar

import clsx from 'clsx';

clsx('foo', 'bar')        // → "foo bar"
clsx('foo', '', 'bar')    // → "foo bar"  (bo'sh string o'tkazilmaydi)
clsx('foo', null, 'bar')  // → "foo bar"  (null o'tkazilmaydi)
clsx('foo', undefined)    // → "foo"      (undefined o'tkazilmaydi)
clsx('foo', false)        // → "foo"      (false o'tkazilmaydi)

false, null, undefined, 0, '' — bularning barchasi e'tiborga olinmaydi. Shart bajarilmasa bo'sh joy qolmaydi.

Ob'ekt — shartli klasslar uchun

clsx({ foo: true, bar: false })  // → "foo"
clsx({ foo: true, bar: true })   // → "foo bar"

// O'zgaruvchilar bilan
clsx({
  'card--active':   isActive,
  'card--disabled': isDisabled,
  'card--error':    hasError,
})

Ob'ektda kalit — klass nomi, qiymat — shart. Qiymat true bo'lsa klass qo'shiladi, false bo'lsa qo'shilmaydi.

Massiv — guruhlash uchun

clsx(['foo', 'bar'])           // → "foo bar"
clsx(['foo', null, 'bar'])     // → "foo bar"
clsx(['foo', ['bar', 'baz']])  // → "foo bar baz"  (ichma-ich massiv)

Aralash — haqiqiy loyihalarda

clsx(
  'btn',                        // har doim
  { 'btn--active': isActive },  // shartli
  size && `btn--${size}`,       // shartli string
  className                     // tashqaridan
)

React da ishlatish

Asosiy — CSS Modules bilan

import clsx from 'clsx';
import styles from './Button.module.css';

function Button({ variant, size, disabled, fullWidth, className, children }) {
  return (
    <button
      disabled={disabled}
      className={clsx(
        styles.btn,
        styles[variant],
        styles[size],
        {
          [styles.disabled]: disabled,
          [styles.fullWidth]: fullWidth,
        },
        className
      )}
    >
      {children}
    </button>
  );
}

[styles.disabled] — computed property: klass nomi o'zgaruvchidan olinadi.

Tailwind bilan

import clsx from 'clsx';

function Alert({ type = 'info', children }) {
  return (
    <div
      className={clsx(
        'flex items-start gap-3 p-4 rounded-lg border text-sm',
        {
          'bg-blue-50  border-blue-200 text-blue-800':  type === 'info',
          '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',
        }
      )}
    >
      {children}
    </div>
  );
}

// Ishlatish
<Alert type="error">Xato yuz berdi</Alert>
<Alert type="success">Muvaffaqiyatli saqlandi</Alert>

cn yordamchi funksiya — Tailwind + clsx + twMerge

Oldingi darsda ko'rgan edik: Tailwind bilan clsx yetarli emas — ziddiyatli klasslar muammosi bor. Shuning uchun tailwind-merge bilan birga ishlatiladi:

// src/lib/utils.js
import clsx from 'clsx';
import { twMerge } from 'tailwind-merge';

export function cn(...inputs) {
  return twMerge(clsx(inputs));
}
import { cn } from '@/lib/utils';

function Input({ error, className, ...props }) {
  return (
    <input
      className={cn(
        'w-full px-3 py-2 border rounded-md text-sm outline-none',
        'focus:ring-2 focus:ring-blue-300 focus:border-blue-500',
        error ? 'border-red-400 focus:ring-red-200' : 'border-gray-300',
        className  // tashqaridan px-5 kelsa — px-3 ni almashtiradi
      )}
      {...props}
    />
  );
}

clsx faqat ishlatiladi degan gapni emas, cn funksiyasi orqali Tailwind ziddiyatlarini ham hal qiladi. Bugungi kunda cn React ekotizimida standartga aylangan — Shadcn/ui, Radix primitives, NextUI kabi kutubxonalarning barchasi shu yondashuvni ishlatadi.


Tez-tez uchraydigan xatolar

String interpolatsiyasini clsx o'rniga ishlatish

// ❌ — shart bajarilmasa "card undefined" bo'ladi
<div className={`card ${isActive && 'card--active'}`}>

// ✅ — clsx false va undefined ni o'tkazmaydi
<div className={clsx('card', isActive && 'card--active')}>

Ob'ektda defisli klass nomi tirnoqsiz

// ❌ SyntaxError — defis JavaScript da kalit sifatida to'g'ridan-to'g'ri yozilmaydi
clsx({ card--active: true })

// ✅ Tirnoqda yoziladi
clsx({ 'card--active': true })

Import unutilishi

// ❌ clsx import qilinmagan — ReferenceError
className={clsx('btn', { active: isActive })}

// ✅
import clsx from 'clsx';

Xulosa

clsx — shartli CSS klasslarni qulay birlashtirish uchun minimal yordamchi. false, null, undefined ni avtomatik o'tkazib yuboradi — className da ortiqcha bo'shliq qolmaydi.

Eslab qoling:

  • clsx — classnames ning engil versiyasi, ikkalasi API jihatdan bir xil
  • String, ob'ekt, massiv — uchala sintaksisni aralashtirib ishlatish mumkin
  • Ob'ektda: { 'klass-nomi': shart } — eng ko'p ishlatiladigan pattern
  • Tailwind bilan — cn = twMerge(clsx(...)) funksiyasi standart yondashuv
  • Defisli klass nomlar ob'ektda tirnoqda yoziladi

Foydali havolalar


Keyingi dars

39-dars: React Router nima, o'rnatish, asosiy tushunchalar