Tailwind UI 드롭다운 메뉴 - React vs Svelte

18546 단어 uireactsveltewebdev
다른 날에는 Next.js를 사용하여 React에서 직장에서 새로운 내부 앱의 프로토타이핑을 하고 있었습니다. 빠르게 시작하기 위해 Tailwind CSS를 사용했습니다. 내 앱에서 간단한 드롭다운 메뉴를 만들어야 했고 그들이 어떻게 했는지에 대한 Tailwind UI 예제를 살펴봤습니다.

실제로 드롭다운 메뉴를 만드는 것은 말처럼 간단하지 않습니다. 먼저 메뉴 외부에서 마우스 클릭을 처리하고 현재 열려 있는 메뉴를 닫아야 합니다. 둘째, Escape 키를 눌러 지원하고 현재 메뉴가 열려 있으면 닫아야 합니다. 셋째, 메뉴에 멋진 애니메이션을 추가하여 더 생생하게 느껴져야 합니다.



React에서 메뉴를 구현하는 것은 내가 기대했던 것만큼 간단하지 않았습니다. Tailwind 스타일 자체는 문제가 아니지만 "클릭 해제"또는 "외부 클릭"기능을 처리하고 Esc 키를 처리하는 방법을 파악하는 데 시간이 좀 걸렸습니다. 그 외에도 React에서 CSS 전환을 수행하는 방법을 연구해야 했습니다. Tailwind의 제작자는 React에 기능이 내장되어 있지 않기 때문에 유용한 코드transition library를 만든 것으로 나타났습니다.

"react click away listener"에 대해 Google search을 수행하는 것은 실제로 도움이 되지 않았습니다. NPM에서 "react click outside"및 "react click away"를 검색하면 필요한 것보다 훨씬 더 많은 결과가 반환되었습니다. 물론 많은 React 라이브러리가 있지만 이를 처리하는 훨씬 간단한 방법이 있어야 한다고 생각했습니다.

다음은 제가 만든 Next.js(React + TypeScript) 코드입니다.

import Link from 'next/link';
import React, { useState, useRef, useEffect } from 'react';
import { Transition } from '@tailwindui/react';

const Menu = ({ user }) => {
  const [show, setShow] = useState(false);
  const container = useRef(null);

  useEffect(() => {
    const handleOutsideClick = (event: MouseEvent) => {
      if (!container.current.contains(event.target)) {
        if (!show) return;
        setShow(false);
      }
    };

    window.addEventListener('click', handleOutsideClick);
    return () => window.removeEventListener('click', handleOutsideClick);
  }, [show, container]);

  useEffect(() => {
    const handleEscape = (event: KeyboardEvent) => {
      if (!show) return;

      if (event.key === 'Escape') {
        setShow(false);
      }
    };

    document.addEventListener('keyup', handleEscape);
    return () => document.removeEventListener('keyup', handleEscape);
  }, [show]);

  return (
    <div ref={container} className="relative">
      <button
        className="menu focus:outline-none focus:shadow-solid "
        onClick={() => setShow(!show)}
      >
        <img
          className="w-10 h-10 rounded-full"
          src={user.picture}
          alt={user.name}
        />
      </button>

      <Transition
        show={show}
        enter="transition ease-out duration-100 transform"
        enterFrom="opacity-0 scale-95"
        enterTo="opacity-100 scale-100"
        leave="transition ease-in duration-75 transform"
        leaveFrom="opacity-100 scale-100"
        leaveTo="opacity-0 scale-95"
      >
        <div className="origin-top-right absolute right-0 w-48 py-2 mt-1 bg-gray-800 rounded shadow-md">
          <Link href="/profile">
            <a className="block px-4 py-2 hover:bg-green-500 hover:text-green-100">
              Profile
            </a>
          </Link>
          <Link href="/api/logout">
            <a className="block px-4 py-2 hover:bg-green-500 hover:text-green-100">
              Logout
            </a>
          </Link>
        </div>
      </Transition>
    </div>
  );
};

export default Menu;


React 구현을 마쳤을 때 Svelte에서 동일한 메뉴를 구현하는 방법에 대해 생각했습니다. 그래서 Svelte로 이식하는 데 약간의 시간이 걸렸습니다.

Svelte의 많은 장점 중 하나는 CSS 전환과 애니메이션이 내장되어 있다는 것입니다. 여기 제가 설명합니다.

<script>
  import { onMount } from 'svelte';
  import { scale } from 'svelte/transition';

  export let user;

  let show = false; // menu state
  let menu = null; // menu wrapper DOM reference

  onMount(() => {
    const handleOutsideClick = (event) => {
      if (show && !menu.contains(event.target)) {
        show = false;
      }
    };

    const handleEscape = (event) => {
      if (show && event.key === 'Escape') {
        show = false;
      }
    };

    // add events when element is added to the DOM
    document.addEventListener('click', handleOutsideClick, false);
    document.addEventListener('keyup', handleEscape, false);

    // remove events when element is removed from the DOM
    return () => {
      document.removeEventListener('click', handleOutsideClick, false);
      document.removeEventListener('keyup', handleEscape, false);
    };
  });
</script>

<div class="relative" bind:this={menu}>
  <div>
    <button
      on:click={() => (show = !show)}
      class="menu focus:outline-none focus:shadow-solid"
    >
      <img class="w-10 h-10 rounded-full" src={user.picture} alt={user.name} />
    </button>

    {#if show}
      <div
        in:scale={{ duration: 100, start: 0.95 }}
        out:scale={{ duration: 75, start: 0.95 }}
        class="origin-top-right absolute right-0 w-48 py-2 mt-1 bg-gray-800
          rounded shadow-md"
      >
        <a
          href="/profile"
          class="block px-4 py-2 hover:bg-green-500 hover:text-green-100"
        >Profile</a>
        <a
          href="/api/logout"
          class="block px-4 py-2 hover:bg-green-500 hover:text-green-100"
        >Logout</a>
      </div>
    {/if}
  </div>
</div>


물론 코드의 양은 React보다 Svelte에서 약간 적지만 인지 부하에 대해서는 어떻습니까? 어느 것이 더 읽기 쉽고 이해하기 쉬운가요? 당신이 판사입니다.

좋은 웹페이지 즐겨찾기