JUNHEE LEE
70
2021-09-25 00:09:01 작성 2021-09-25 00:23:52 수정됨
2
148

react useState로 관리하는 배열에 dom api 적용하기 질문입니다!


기본 코드는 다음과 같습니다.

현재 CSR인 상황에서 html을 로드하기전에 js를 먼저 불러오는 경우 때문에 window.onload를 통해 감싸줘야 document.querySelector를 통해 선택한 $fruits 변수가 비어있지 않은 상태에서 addEventListener를 사용할 수 있습니다.

const App2 = () => {
  window.onload = () => {
    const $fruits = document.querySelector(".fruits");

    $fruits.addEventListener("click", function (e) {
      console.log(e.target);
      $fruits.removeChild(e.target);
    });
  };

  return (
    <ul className="fruits">
      <li className="fruit">orange</li>
      <li className="fruit">apple</li>
      <li className="fruit">banana</li>
      <li className="fruit">pine apple</li>
    </ul>
  );
};

export default App2;

이벤트 위임을 사용하여 부모 요소인 fruits를 선택하여 이벤트의 타깃인 e.target을 자식 요소에서 제거하는 로직입니다.


하지만 여기서 한단계 더 나아가 state에 배열을 담고, 이 state 배열을 매핑한 상태에서 이벤트 위임을 사용하고 싶은 상황입니다.

const App2 = () => {
  const [list, setList] = useState(["orange", "apple", "banana", "pine apple"]);

  useEffect(() => {
    console.log("list:", list);
  }, [list]);

  window.onload = () => {
    const $fruits = document.querySelector(".fruits");

    $fruits.addEventListener("click", function (e) {
      console.log(e.target);
      $fruits.removeChild(e.target);
      setList(list.filter((v) => v !== e.target.innerHTML));
    });
  };

  const onRemove = useCallback(() => {}, []);

  return (
    <ul className="fruits" onClick={???}>
      {list?.map((v, i) => (
        <li className="fruit" key={i}>
          {v}
        </li>
      ))}
    </ul>
  );
};

export default App2;

이때 똑같이 이벤트 위임을 사용하여

- fruits 이름을 가지는 ul에 onClick={ } 이벤트를 통해

- map을 돌고 있는 li 요소를 지우고, setList로 list에서 해당 요소를 필터링하고 싶은 상황입니다

① 그런데 map을 통해 렌더링을 해서 인지, 요소를 클릭할 경우 dependencies를 가지고 있는 list가 계속 콘솔에 등차 수열 처럼 찍히고 ( 0개, 1개, 2개, ... , n개)

② 오류 속에서 필터링이 되더라도 다음 값을 지우면 다시 초기값에서 지워진 요소만 보여지는...? 현상이 나타납니다.






DOM API를 사용했던 이유는 이전에 현재 '사이드 프로젝트' 진행 시에 해시태그를 DOM API를 사용하여 동적으로 만들어주어서, 이를 받아와 useState에 담고, 이를 수정하는 상황에서 '기존 해시태그를 지우면서 state에 담긴 배열을 빼야하는 상황'에서 문제가 생겨 조금 간단하게 풀어보고자 위와 같이 코드를 구성해보았습니다.

제가 이해가 안가는 부분은

- ① window.onload 때문에 생기는 이벤트 위임시에 필요한 onRemove 함수를 만들 때 생기는 과정에서 코드를 어떻게 구성해야할지?

- ② setList를 통해 state를 조작할 때 filter를 사용하는 게 맞는지..? (splice도 해보고 filter도 해보고 다 해보는 중입니다 ㅠㅠ)


const App2 = () => {
  const [list, setList] = useState(["orange", "apple", "banana", "pine apple"]);

  useEffect(() => {
    console.log("list:", list);
  }, [list]);

  const onRemove = useCallback(() => {
    console.log("clicked!");
    window.onload = () => {
      const $fruits = document.querySelector(".fruits");

      $fruits.addEventListener("click", function (e) {
        console.log(e.target);
        $fruits.removeChild(e.target);
        setList(list.filter((v) => v !== e.target.innerHTML));
      });
    };
  }, [list]);

  return (
    <ul className="fruits" onClick={onRemove}>
      {list?.map((v, i) => (
        <li className="fruit" key={i}>
          {v}
        </li>
      ))}
    </ul>
  );
};

export default App2;

이런식으로 코드를 진행하니, clicked! 까지만 걸리고 밑에 코드로 들어가지 않습니다 ㅜ

해당 방식이 잘못되었다면 '어떤 방식으로 해보세요'라고 던져주셔도 좋을 것 같습니다..

0
  • 답변 2

  • 잉랑동딩
    112
    2021-09-25 02:05:54 작성 2021-09-25 02:08:52 수정됨

    1. window.onload 말고 useEffect를 사용하세요


    2. 

     $fruits.removeChild(e.target);
            setList(list.filter((v) => v !== e.target.innerHTML));

    부분에서 결국 li 부분을 2번 지우는 행동이라 문제가 발생할 확률이 너무 높습니다.


    3. addEventListener는 사용 할 때 반드시 removeEventListener과 함께 사용해주세요


    추가적으로 2번 질문에 대한 답변은 위와 같은 경우에는 filter를 사용할 것 같습니다.

    ex) setList((prev)=>prev.filter(...)) or setList(list.filter(...)) 

    아래에 같은 동작을하는 참고 코드를 가볍게 만들어 봤는데 스스로 생각해보시고 참고해주세요








    import {  useEffectuseState } from "react";

    export default function Test () {
      const [listsetList] = useState(["orange""apple""banana""pine apple"]);

     

      useEffect(() => {
          const handleRemove = (e:any)=>{
            setList(list.filter((v=> v !== e.target.innerHTML));
          }

          const $fruits = document.querySelector(".fruits");
      

          if($fruits){
          $fruits.addEventListener("click",handleRemove );
        
          return () => {
            $fruits.removeEventListener("click",handleRemove)
          }
        }
      }, [list]);

      return (
        <ul className="fruits" >
          {list.map((vi=> (
            <li className="fruit" key={i}>
              {v}
            </li>
          ))}
        </ul>
      );
    }
        

  • JUNHEE LEE
    70
    2021-09-25 08:25:46 작성 2021-09-25 08:34:39 수정됨

    ① useEffect를 왜 인지 변수를 감지하는 용도로만 썼던 것 같습니다.. 따라서 useEffect 내부에서 클린업을 통해 무언가를 할 것이라고 사고를 안해봤던 것 같습니다.

    ② DOM API를 사용해서 조작해보는 경험이 익숙치 않아서 원하는 작업이 완료됐을 때, removeEventListener를 사용하는 부분에 대해서 인지를 못했던 것 같습니다

    ③ useEffect를 통해 componentDidMount()의 역할은 하는 것으로 볼 수 있겠군요.. 너무 오류만 구글링해서 사용하다 보니, 깊이 있는 리액트 hook의 동작 방식에 대해서 알지 못했던 것 같습니다

    이 부분에 대해서도 더 찾아봐야겠네요..!!

    부족한 부분이 어떤 부분인지 보이는 것 같습니다.. 정말 정말 감사합니다!!!! 

  • 로그인을 하시면 답변을 등록할 수 있습니다.