반응형
import React from "react";
import useCounter from "./useCounter"; // 위에서 만든 useHook 

function CounterComponent() {
  const { count, increment, decrement, reset } = useCounter(5);
  return (
    <div>
      <h2>Count: {count}</h2>
      <button onClick={increment}>➕Increase</button>
      <button onClick={decrement}>➖Decrease</button>
      <button onClick={reset}>🔄Reset</button>
    </div>
  );
}

export default CounterComponent;

React에서 Custom Hook은 useState, useEffect, useRef 등의 기본 Hook을 조합하여 재사용 가능한 로직을 만들어 사용할 수 있게 해줍니다.
커스텀 훅의 네이밍 규칙은 "use"로 시작해야 하며, 내부에서 hook을 호출할 수 있습니다.

 

useHook의 기본구조를 살펴보면 아래와 같습니다.

import { useState, useEffect } from "react";

function useCustomHook() {
  const [state, setState] = useState(null);

  useEffect(() => {
    console.log("Custom Hook Mounted!");

    return () => {
      console.log("Custom Hook Unmounted!");
    };
  }, []);

  return [state, setState];
}

 

위의 구조를 참고해서 counter hook 을 만들고 호출도 해봅시다.

// 커스텀 useCounter 

import { useState } from "react";
function useCounter(initialValue = 0) {
  const [count, setCount] = useState(initialValue);
  const increment = () => setCount((prev) => prev + 1);
  const decrement = () => setCount((prev) => prev - 1);
  const reset = () => setCount(initialValue);
  return { count, increment, decrement, reset };
}
export default useCounter;

 

이렇게 나만의 custom hook을 사용해서 재사용성을 높이고, 코드 분리를 하여 코드를 최척화 가능합니다. 

저는 모든 플젝에서 많이 사용하고 있습니다. 😉

반응형
반응형

퀴즈관련된 로직을 만들던 중 한글 초성 맞추기 관련 로직을 찾아보게되었습니다. 

흔히 쓰지 않는 로직이지만 이참에 정리해 두려고 합니다. 

 

한글의 초성을 추출하기 위해서는 각 한글 문자를 유니코드로 변환한 후, 해당 유니코드에서 특정 값을 빼서 초성의 유니코드를 얻어야 합니다. 그리고 그 유니코드를 다시 문자로 변환하여 초성을 얻을 수 있습니다.

 

const [extractInput, setExtractInput] = useState("");
const [extractChar, setExtractChar] = useState("");
const getInitials = (e) => {
    setExtractInput(e.target.value);
    const str = e.target.value;
    const cho = ["ㄱ", "ㄲ", "ㄴ", "ㄷ", "ㄸ", "ㄹ", "ㅁ", "ㅂ", "ㅃ", "ㅅ", "ㅆ", "ㅇ", "ㅈ", "ㅉ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ"];
    let result = "";
    for (let i = 0; i < str.length; i++) {
        const charCode = str.charCodeAt(i);
        if (charCode >= 0xAC00 && charCode <= 0xD7A3) {
            const uni = charCode - 0xAC00;
            const choIdx = Math.floor(uni / (21 * 28));
            result += cho[choIdx];
        } else {
            result += str[i];
        }
    }
    setExtractChar(result);
}

return(
	<div>
        <input type="text" value={extractInput} onChange={getInitials} style={{border: "1px solid lightgrey"}} />
        <br/>
        <span>range value: {extractChar}</span>
    </div>
)

 

입력된 문자열의 각 문자에 대해 유니코드를 계산하여 초성을 추출하고, 그 결과를 state에 업데이트 해주면됩니다. 

 

charCode >= 0xAC00 && charCode <= 0xD7A3 이 부분은 한글 유니코드의 범위를 체크하는 조건입니다.

0xAC00은 '가'의 유니코드 값으로, 이는 한글 음절의 시작점이며, 초성이 'ㄱ', 중성이 'ㅏ', 종성이 없는 경우를 나타냅니다.
0xD7A3은 '힣'의 유니코드 값입니다. 이는 한글 음절의 끝점이며, 초성이 'ㅎ', 중성이 'ㅣ', 종성이 'ㅎ'인 경우를 나타냅니다.
즉, charCode >= 0xAC00 && charCode <= 0xD7A3 이 조건은 주어진 문자가 한글 음절인지를 확인합니다. 
이 조건이 true이면, 해당 문자는 '가'부터 '힣' 사이의 한글 음절이라는 것을 알 수 있습니다.

이 범위 내의 문자에 대해서만 초성을 추출하는 로직을 적용합니다. 만약 문자가 이 범위 밖에 있다면, 그 문자는 한글 음절이 아니므로 초성 추출 로직을 적용하지 않고, 원래 문자를 그대로 반환합니다.
만약 한글이 아닌 다른 글자가 들어가는경우 그대로 return 하게 됩니다.

반응형
반응형

기존에는 document.execCommand('copy') 를 사용해서 구현을 했었는데, deprecated 되었다고 합니다. 

그래서 다른 방법을 찾아보다가 useRef 를 사요해서 navigator.clipboard.writeText() 함수를 사용했는데, 로컬에서는 잘 되다가 배포이후 안되서 찾아보니 보안상의 이유로 https 에서는 복사가 되지 않는다고 하네요. 

 

그래서 결국 라이브러리를 사용했습니다. "clipboard-copy"라는 라이브러리 입니다. 

먼저 설치를 해줍니다. 

npm install clipboard-copy

 

그리고 copy라는 함수를 import 해옵니다. 

이 copy라는 함수로 useRef를 사용하여 input의 value값을 클립보드에 복사하기를 할 껍니다. 

 

import copy from 'clipboard-copy';

const App = () => {
  const inputRef = useRef();
  const handleCopy = () => {
    copy(inputRef.current.value);
    alert('복사되었습니다!');
  };

  return (
    <div>
      <input ref={inputRef} type="text" placeholder="복사할 문구를 적으세요~" />
      <button onClick={handleCopy}>COPY!</button>
    </div>
  );
}

결과화면

반응형
반응형

대부분의 UI요소의 반응형은 css로도 처리가 가능합니다.

하지만 간혹 js 내에서 구조를 다시 그린다거나, className을 pc냐 모바일이냐에 따라 다시 그린다거나 하는 일이 자주 발생하기 때문에 js 내에서도 window의 사이즈를 실시간으로 알아야 하는 것이 필요로 합니다. 

 

React에서는 useEffect와 window의 size의 변화를 addEventListener함수를 통해 알 수 있습니다. 

 

import React, {useEffect, useState} from 'react'

const WindowWidth = () => {
  const [windowWidth, setWindowWidth] = useState(window.innerWidth)
  const handleResize = () => {
    setWindowWidth(window.innerWidth)
  }
  useEffect(() => {
    window.addEventListener('resize', handleResize)
    // 컴포넌트 언마운트 시 이벤트 리스너 삭제
    return () => {
      window.removeEventListener('resize', handleResize)
    }
  }, [])

  return (
    <div>
      <p>Window Width: {windowWidth}px</p>
    </div>
  )
}

export default WindowWidth

 

1.  window.innerWidth : window의 width 값을 알 수 있습니다. 

2. return () => { window.removeEventListener('resize', handleResize) } : 컴포넌트가 DOM에서 제거될때 실행되는 클린업 함수입니다. 이걸 추가해 줘야 메모리 누수를 방지할 수 있습니다. 

 


이외 특정 width 값 이하일때 mobile로 구분하도록 해보겠습니다. 

저는 460을 기준으로 이하일때는 모바일 이상일 때는 pc라고 가정했습니다. 

 

import React, {useEffect, useState} from 'react'

const WindowWidth = () => {
  const [isMobile, setIsMobile] = useState(false)

  const handleResize = () => {
    window.innerWidth < 461 ? setIsMobile(true) : setIsMobile(false)
  }
  useEffect(() => {
    window.innerWidth < 461 ? setIsMobile(true) : setIsMobile(false)
    window.addEventListener('resize', handleResize)
    // 컴포넌트 언마운트 시 이벤트 리스너 삭제
    return () => {
      window.removeEventListener('resize', handleResize)
    }
  }, [])

  return (
    <div>
      <p style={{color: `${isMobile ? "blue": "red"}`}}>
        {isMobile ? 'MOBILE' : 'PC'}
      </p>
    </div>
  )
}

export default WindowWidth

 

useEffect 내에 addEventListener로 연결하기 전

window.innerWidth < 461 ? setIsMobile(true) : setIsMobile(false)

이 부분을 한번 더 넣어주는 이유는 최초 실행 후 window의 resize가 일어날 때만 값이 바뀌기 때문에,

최초 화면이 mobile 화면이어도 초기 세팅값이 false이기 때문에 pc로 나옵니다. 

이렇게 resize가 일어나지 않았을 때 최초 렌더링 될 때 한번 체크를 해주기 위해 추가해 줬습니다. 

결과화면

은근히 자주 사용하는 window 실시간 width값 가져오기였습니다~

반응형
반응형

실제 프로젝트를 하다보면, 다양한 케이스가 존재합니다. 

우리가 보통 생각하는 map 은 데이터가 이미 존재하고 해당 데이터를 map 으로 돌린다. 

특히 리액트 프로젝트에서 return 부분에서는 for문을 사용할 수 없다.

라고 인식하기 때문에, 특정 숫자를 지정해서 반복문을 돌려야 하는 경우 불가능 하다고 생각하는 분들도 있을 것 같습니다. 

 

방법은 여러가지가 있습니다만, 만약 배열을 돌리는 수가 적은 경우 그냥 [1,2,3] 로 돌려도 됩니다. 

<div>
    {[1,2,3].map((_, idx) => (
      <p key={idx}>item {idx}</p>
    ))}
</div>

하지만, 돌려야하는 그 수가 많은경우 저렇게 사용하면 비효율적이기 때문에, Array함수를 사용하여 배열을 만들어 사용하는것이 좋습니다. 단, Array(5) 이렇게 하면 5자리 배열이 생성되는건 맞지만 각각의 요소는 undefined 이므로, 대신 fill로 대충 값을 채워준뒤 사용하면 됩니다. 

<div>
    {Array(5).fill(null).map((_, idx) => (
      <p key={idx}>item {idx}</p>
    ))}
</div>

결과화면

 

그러면 이를 이용해서 다음과 같은 배열도 만들수 있습니다. 

 

var menu=["햄버거", "치킨", "피자"]; 라는 데이터가 있는데,

무조건 5개의 UI가 나와야하고, 값이 없는경우는 "준비중"으로 표시한다고 가정해 봅시다.

그러면 우리는 위에서 배운 Array를 사용해서 구현할 수 있겠습니다. 

 

※ 아래 코드를 보기전에 문제처럼 먼저 구현해보면 좋을 것 같네요!😀

 

function App() {
  const menu = ["햄버거", "치킨", "피자"];
  return (
    <div>
      <ul>
        {
          menu.map((item) => (
            <li>{item}</li>
          ))
        }
        {
          Array(5 - menu.length).fill(0).map((item, idx) => (
            <li>준비중</li>
          ))
        }
      </ul>
    </div>
  )
}

 

결과화면

 

반응형
반응형

react 프로젝트를 할 때 많이 사용되는 로직 중 하나가 검색입니다. 

저도 매번 프로젝트를 할때마다 많이 사용되기 때문에 이참에 따로 정리해서 만들어 두려고 한다. 

 

이전에 올렸던 filter 라는 함수를 사용해서 구현한 예정이다. 

참고로 filter는 react 문법이 아닌, javascript 문법으로 배열을 다룰 때 사용한다. 

https://jotokkiplayground.com/1

 

배열을 다루는 forEach, map, filter

1. ForEach 함수 forEach 메서드는 배열의 각 요소에 대해 주어진 함수를 실행하며, 반환값은 없습니다. 이 메서드는 배열의 각 요소에 대해 특정 작업을 수행하고자 할 때 사용됩니다. 즉, forEach는 배

JOTOKKIPLAYGROUND.COM

 

우선 필요한 state는 검색어를 입력하는 searchInput 이 필요합니다. 

이 state 의 값을 기준으로 기존의 배열을 filter를 통해 검색어가 포함된 문자열 기준으로 새롭게 그려진 배열을 filteredItems 안에 담고, 이 filteredItems를 아래 map으로 돌려줄 겁니다.

 

import React, {useState} from 'react'

function SearchableList() {
  const [searchInput, setSearchInput] = useState('')
  const items = ['사과', '바나나', '체리', '나비', '사자', '독수리', '수박']
  const filteredItems = items.filter((item) =>
    item.toLowerCase().includes(searchInput.toLowerCase()),
  )

  return (
    <div>
      <input
        type="text"
        placeholder="검색어를 입력하세요."
        value={searchInput}
        onChange={(e) => setSearchInput(e.target.value)}
      />
      <ul>
        {filteredItems.map((item) => (
          <li key={item}>{item}</li>
        ))}
      </ul>
    </div>
  )
}

export default SearchableList

결과화면

 

 

만약 이 데이터가 단순 배열이 아니고 오브젝트 형태이거나, 

검색어와 노출되는 값이 다른경우 그냥 searchInput에 입력되는 검색어를 기준으로 filter를 걸어주고 return 되는 UI 부분에서만 노출되는 값만 rendering 해주면 됩니다~! 

 

import React, {useState} from 'react'

function SearchableList() {
  const [searchInput, setSearchInput] = useState('')
  const items = [
    {id: '사과', value: "🍎"}, 
    {id: '바나나', value: "🍌"}, 
    {id: '체리', value: "🍒"}, 
    {id: '나비', value: "🦋"}, 
  ]
  const filteredItems = items.filter((item) =>
    item.id.toLowerCase().includes(searchInput.toLowerCase()),
  )

  return (
    <div>
      <input
        type="text"
        placeholder="검색어를 입력하세요."
        value={searchInput}
        onChange={(e) => setSearchInput(e.target.value)}
      />
      <ul>
        {filteredItems.map((item) => (
          <li key={item.id}>{item.value}</li>
        ))}
      </ul>
    </div>
  )
}

export default SearchableList

결과화면

 

참고로 중간에 toLowerCase가 포함되어있는데, 이는 만약 검색해야 할 데이터가 영문인 경우 대소문자에 따라 같은 값을 찾을 수 없기 때문에, 주로 사용합니다. 사실 현재 예시는 한국어이므로 필요 없지만, 나중에 필요할 때 사용하려고 그냥 넣어놓았습니다. 

쉽게 apple 과 Apple을 두 문자열 모두 toLowerCase로 apple, apple 만들어놓고 비교를 하기 위함입니다~

만약 대소문자도 구분하고 싶다면  toLowerCase는 삭제해야 합니다.

반응형

+ Recent posts