Today Sangmin Learned
article thumbnail
728x90
const termSubmit = document.getElementById("btn_add_term");
const termList = document.getElementById("list_terms");
const termBox = document.getElementById("text_term");
const termLi = document.querySelectorAll("ul li");

termSubmit.addEventListener("click", () => {
  addRelatedTerm(termBox.value);
}, false);

termBox.addEventListener("keyup", (e) => {
  const keyCode = e.keyCode;
  if (e.keyCode == 188 || e.keyCode == 32 || e.keyCode == 13) {
    addRelatedTerm(termBox.value);
  }
});

function addRelatedTerm(name) {
  const special = /[^ㄱ-힣a-zA-Z0-9+#]/gi;
  const newTerm = name.replace(/[^ㄱ-힣a-zA-Z0-9+#]/gi, "");
  if (!newTerm) {
    return;
  }
  console.log(newTerm);
  const trimmedTerm = newTerm.trim();

  const liElem = document.createElement("li");
  liElem.innerText = trimmedTerm;

  const removeBtn = document.createElement("button");
  removeBtn.innerText = 'X';
  removeBtn.addEventListener('click', () => {
    removeBtn.parentNode.removeChild(removeBtn);
    liElem.parentNode.removeChild(liElem);
  })

  const items = document.querySelectorAll("#list_terms > li");
  for (let i = 0; i < items.length; i++) {
    if (items[i].innerHTML.split('<')[0] === newTerm) {
      alert('중복 단어');
      termBox.value = '';
      return;
    }
  };
  termList.appendChild(liElem).appendChild(removeBtn);
  termBox.value = '';
  return;
}
addRelatedTerm("**(*(*(*");

요새 리액트를 배우고 있는데, 이 것을 써먹을 곳이 좀 필요했다. 그래서 위의 JS 코드를 리액트로 리팩토링 해봐야겠다는 생각이 들었다. 그리하여 새로운 React app을 만들고, 컴포넌트를 짜봤다.

import React, { useState } from "react";
import "./App.css";

const TermRelated = () => {
  const [relatedTerms, setRelatedTerms] = useState([{ id: "", text: "" }]);
  const [inputTerm, setInputTerm] = useState("");
  const [nextId, setNextId] = useState("");

  const onChange = (e) => setInputTerm(e.target.value);

  const onClick = () => {
    const nextRelatedTerms = relatedTerms.concat({
      id: nextId,
      text: inputTerm,
    });
    setNextId(nextId + 1);
    setRelatedTerms(nextRelatedTerms);
    setInputTerm("");
  };

  const onKeyPress = (e) => {
    if (
      (e.charCode === 188 || e.charCode === 13 || e.charCode === 32) &&
      e.target.value
    ) {
      onClick();
    }
  };

  const onRemove = (id) => {
    const nextRelatedTerms = relatedTerms.filter(
      (relatedTerms) => relatedTerms.id !== id
    );
    setRelatedTerms(nextRelatedTerms);
  };

  const relatedTermsList = relatedTerms.map((relatedTerm) => (
    <>
      <li key={relatedTerm.id}>
        {relatedTerm.text}
        <button onClick={() => onRemove(relatedTerm.id)}>X</button>
      </li>
    </>
  ));
  return (
    <>
      <input
        value={inputTerm}
        onChange={onChange}
        onKeyPress={onKeyPress}
        maxLength="15"
      />
      <button onClick={onClick}>추가</button>
      <ul id="list_terms">{relatedTermsList}</ul>
    </>
  );
};

export default TermRelated;

ul 내 각 li마다 id와 text 각각을 부여하고, 이 id는 X키를 눌러서 li를 지울 때 사용한다.
onClick을 했을 때,

  1. concat을 이용하여, 새로운 inputTextnextId를 가진 값을 배열에 합친다.
  2. nextId에 1을 더한 값을 새로운 nextId로 설정한다.
  3. setRelatedTerms를 이용해 nextRelatedTerms의 값을 반영한다.
  4. inputText의 값을 비워준다.

이 onClick 함수가 핵심이다. 엔터 및 스페이스바를 눌렀을 때도 추가가 되게끔 구현하고자 onKeyPress 이벤트에 각각의 charCode를 추가했다. 이 때, 아무 값도 없을 때에도 <li></li> 와 같이 추가되는것을 방지하고자 () && e.target.value를 추가해줬다.
그리고, onRemove 함수를 통해 relatedTerms.id와 지우려는 id가 같지 않은 경우를 제외하는 코드를 추가했고, 이를 li 내에 button을 만들어서 그쪽에다가 명령을 줬다.
그러나, 아직 완성되지 않았기 때문에 계속 더 해봐야 한다.

알게 된 점

  1. 리액트에서는 각 키당 번호를 이용해서 코딩하려면 e.keyCode가 아니라 e.charCode를 이용해야 한다.

궁금한 점

  1. 바닐라 JS로 코딩했을 때는 엔터 키가 두 번 눌리는 문제가 있었다. 근데, 리액트로 코딩을 하니까 영문일 때는 스페이스바를 누르면 공백이 추가되지 않은 상태로 바로 명령이 실행되지만 한글일 때는 스페이스바가 추가되고 난 후 한 번 더 스페이스바를 눌러야 추가가 된다. 이게 왜 그런지 잘 모르겠다.

앞으로 더 해야 할 점

  1. replace문을 사용하여 input값에 특수문자 및 공백이 들어가지 못하게 하기
  2. 중복을 어떻게 체크하고 방지할 것인가?
profile

Today Sangmin Learned

@steadily-worked

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!