Как стать автором
Обновить

Эмулируем React useState в обычном JS (via data-attributes & css selectors)

Время на прочтение3 мин
Количество просмотров6.3K

Добрый день, Хабр! Решил поделиться своим небольшим, но полезным открытием в плане использования html data-attributes & css selectors.

Html data-attributes - это кастомные атрибуты, которые вы можете сами назначать куда-угодно и с каким угодно именем (но имя должно начинаться с префикса data-). Затем вы можете использовать их в css селекторах, чтобы влиять на содержимое классов и уже классами управлять элементами. Движок браузера автоматически среагирует на изменение data-атрибута и применит соответствующий код css класса.

Код реакт компоненты, где по нажатию кнопки что-то скрывается, что-то показывается:

import React, {useState} from "react";

const ComponentWithExpandableContent = () => {
    const [expanded, setExpanded] = useState(false)

    return <div style={{
        display: 'flex',
        flexFlow: 'column nowrap',
        alignItems: 'start',
        padding: 32,
        gap: 32,
    }}>

        { !expanded && <div>Initial content</div> }

        <button onClick={()=>setExpanded(!expanded)}>
            { !expanded ? 'Expand' : 'Collapse' }
        </button>

        { expanded && <div>Additional content</div> }
        { expanded && <div>More additional content</div> }

    </div>
}
export default React.memo(ComponentWithExpandableContent)

Состояние expanded=false:

Состояние expanded=true:

Теперь просто html css js.

Файл emulating-use-state.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>
    Эмулируем React useState в обычном JS 
    (via data-attributes & css selectors)
  </title>
  <link rel="stylesheet" type="text/css" href="emulating-use-state.css">
</head>
<body>

<script>
  function toggleExpand(){
    const element = document.getElementById('component')
    if (element.dataset.expanded!=='true'){
      element.dataset.expanded = 'true'
    } else {
      element.dataset.expanded = 'false'
    }
  }
</script>

<div class="someElementContext">
  <!-- data-expanded хранит состояние -->
  <div id="component" class="component" data-expanded="false">
    
    <div class="collapsableContent">
      <div>Initial content</div>
    </div>
    
    <button onClick="toggleExpand()">
      <div class="collapsableContent">Expand</div>
      <div class="expandableContent">Collapse</div>
    </button>
  
    <div class="expandableContent">
      <div>Additional content</div>
    </div>
    <div class="expandableContent">
      <div>More additional content</div>
    </div>
    
  </div>
</div>

</body>
</html>

Файл стилей emulating-use-state.scss:

.someElementContext {
  display: contents;
  
  .component {
    display: flex;
    flex-flow: column nowrap;
    align-items: start;
    padding: 32px;
    gap: 32px;
    
    // Классы, управляющие видимостью элементов на странице
    // в зависимости от текущего значения data-expanded
    .expandableContent {
      display: none;
    }
    .collapsableContent {
      display: contents;
    }
    &[data-expanded=true] .expandableContent {
      display: contents;
    }
    &[data-expanded=true] .collapsableContent {
      display: none;
    }
  }
}

Сгенерированный средой разработки emulating-use-state.css:

.someElementContext {
  display: contents;
}
.someElementContext .component {
  display: flex;
  flex-flow: column nowrap;
  align-items: start;
  padding: 32px;
  gap: 32px;
}
.someElementContext .component .expandableContent {
  display: none;
}
.someElementContext .component .collapsableContent {
  display: contents;
}
.someElementContext .component[data-expanded=true] .expandableContent {
  display: contents;
}
.someElementContext .component[data-expanded=true] .collapsableContent {
  display: none;
}

Получилось поведение аналогичное описанному в реакт компоненте.

Здесь в качестве стэйта выступает кастомный data-атрибут data-expanded - он и хранит состояние. А css селектор [data-expanded=true] изменяет свойство display из none в contents (display: contents означает показывать содержимое элемента так, как будто самого элемента не существует) и обратно в специальных классах-обёртках .expandableContent & .collapsableContent, которыми можно просто обернуть любые элементы в html разметке.

Теги:
Хабы:
+1
Комментарии19

Публикации

Истории

Работа

Ближайшие события