Как реализовать табы в Реакте, не используя хуки

Рейтинг: 1Ответов: 1Опубликовано: 30.01.2023

Создал табы на чистом CSS: https://codepen.io/syberian/pen/RwBBqGq - и эти табы отлично работают в песочнице.

HTML

<div class='match-outcome'>
   <ul class='tabs'>
     <li class='tab'>
       <input class='switcher' type="radio" id="tab-1" name="tabs" checked />
       <label for="tab-1">Победы</label>
       <div class="tab-content">
         <p>ПОБЕДЫ.</p>
       </div>
     </li>

     <li class='tab'>
       <input class='switcher' type="radio" id="tab-2" name="tabs" />
       <label for="tab-2">Поражения</label>
       <div class="tab-content">
         <p>ПОРАЖЕНИЯ.</p>
       </div>
     </li>

     <li class=tab>
       <input class='switcher' type="radio" id="tab-3" name="tabs" />
       <label for="tab-3">Ничьи</label>
       <div class="tab-content">
         <p>НИЧЬИ.</p>
       </div>
     </li>
   </ul>
</div>

СSS

.match-outcome { 
    max-width: 684px;
    margin: 0 auto; 
    padding: 6rem 0 2.5rem;
}
    
.tabs {
    position: relative;
    min-height: 315px;
    display: flex;
    margin: 0;
    padding: 0;
    list-style: none;
    overflow: hidden;
}
    
.tab {
    flex: 1;
}
    
.tab label {
    display: block;
    box-sizing: border-box;
    height: 32px;
    font-family: 'Montserrat', sans-serif;
    font-size: 1.5rem;
    font-weight: 600;
    line-height: 2rem;
    letter-spacing: 0em;
    text-align: center;
    cursor: pointer;
    transition: background 0.6s ease;
}
    
.tab label:hover {
    color: lightblue;
}
    
.tab-content {
    position: absolute;
    left: 0; 
    bottom: 0; 
    right: 0;
    top: 32px;
    padding-top: 1rem;
    background: #cfc0e3;
    transition: opacity 0.6s ease, transform 0.6s ease;
    opacity: 0;
    transform: scaleX(0);
    transform-origin: top center;
}
    
.tab [type=radio] { 
    display: none; 
}
    
.switcher:checked ~ label {
    color: blue;
}
    
.switcher:checked ~ .tab-content {
    opacity: 1;
    transform: scaleX(1);
}

Но эти табы не работают в Реакте. Проблема заключается в том что одному из табов в разметке установлен атрибут checked (убрать его я не могу, он задаёт начальный контент, при загрузке странице). Из-за этого при ререндере компонента, checked всегда стоит на первом табе и вкладки переключаются не корректно. При нажатии на любую вкладку, кроме первой, переключение почему-то происходит только со ВТОРОГО раза. А при первом нажатии всегда перебрасывает на первую вкладку.

Хуки я не хочу ПОКА использовать, т.к. это реакт-компонент на самом деле будет использоваться в NextJS. И мне почему-то сказали чтобы я пока не использовал хуки, а просто верстал компоненты, без сложной логики.

Есть ли какой-то простой способ решить эту проблему? Можно ли без хуков, написать код на TS который реализует нужный мне функционал табов? Я пока пытаюсь сообразить, как с помощью TS переключать на каждом табе checked={true} и checked={false}.

UPD. По просьбам из комментариев, публикую JSX с по сути такой же разметкой как и выше, если не учитывать особенности синтаксиса в реакте. СSS не выкладываю, т.к. он уже выше был скопирован как раз из рабочего проекта.

import styles from './Styles.module.css'

<div className={styles['match-outcome']}>
  <ul className={styles.tabs}>
    <li className={styles.tab}>
      <input className={styles.switcher} type="radio" id="tab-1" name="tabs" checked={true} />
      <label htmlFor="tab-1">Победы</label>
      <div className={styles["tab-content"]}>
        <p>ПОБЕДЫ.</p>
      </div>
    </li>
    
    <li className={styles.tab}>
       <input className={styles.switcher} type="radio" id="tab-2" name="tabs" />
       <label htmlFor="tab-2">Поражения</label>
       <div className={styles["tab-content"]}>
          <p>ПОРАЖЕНИЯ.</p>
       </div>
     </li>
    
     <li className={styles.tab}>
       <input className={styles.switcher} type="radio" id="tab-3" name="tabs" />
       <label htmlFor="tab-3">Ничьи</label>
       <div className={styles["tab-content"]}>
          <p>НИЧЬИ.</p>
       </div>
     </li>
   </ul>
</div>

Ответы

▲ 2Принят

В коде небольшой конфликт между управляемыми и не управляемыми элементами input.

checked - используется в управляемых, когда значение устанавливается снаружи. Если в итоге оно будет задаваться через переменные можно так и оставить и все само исправится, когда появится состояние.

Для решения сейчас можно заменить на defaultChecked - который используется в неуправляемых компонентах, и состояние инпута хранится внутри него.