[TIL] 221004

먼지·2022년 10월 4일
1

TIL

목록 보기
35/57
post-thumbnail

오늘한것

1.외부클릭 감지 모달창
우선 내가 모달이란 개념을 잘 모르는 것 같다. 그냥 화면 어딘가에서 부모의 위에 떠있는 상태?를 다 모달 느낌으로 생각해서 뭔가 거대해진 것 같다.. 조금 찾아보니 모달이란 용어는 보통 UI 쪽에서 다이얼로그나 팝업으로 가리키는 영역을 의미(링크) 한다고 한다!

내가 구현하려던 건 게시글이나 댓글을 작성한 유저와 현재 로그인 된 유저가 같다면 수정이나 삭제를 할 수 있게 토글 버튼이 보인다. 그 버튼을 누르면 수정하기, 삭제하기 버튼이 보이고 부모 element의 외부(토글 버튼 포함)를 클릭했을 때 닫혀야 하는데 이걸 한 3일을 수정한 것 같다.

처음에 했던 방법이 아마 이랬었는데

// Modal.tsx
import { useEffect, useState } from 'react';

export default function Modal({ isOpen, setIsOpen, }) {
  const modalRef = useRef() as React.RefObject<HTMLElment>;
  
  const clickToggleOutside = (event: any) => {
    if (isOpen && !toggleRef.current?.contains(event.target)) {
      setIsOpen(false);
    }
  };

  useEffect(() => {
    if (toggle) {
      document.addEventListener('mousedown', clickToggleOutside);
    }
    return () => {
      document.addEventListener('mousedown', clickToggleOutside);
    };
  }, []);

  return isOpen ? <div ref={modalRef}>{children}</div> : null
}

// Post.tsx
export default function Post() {
  const [isToggleOpen, setIsToggleOpen] = useState(false);
  ...
  return (
    <>
      ...
      <button>토글</button>
      <Modal isOpen={isToggleOpen} onClick={() => setIsToggleOpen(prev => !prev)}>
        <button>수정하기</button>
        <button>삭제하기</button>
      </Modal>
    </>
  )
}

button의 onClick에는 이전의 toggleState와 반대의 boolean 값으로 변경하는 함수가 들어가 있는데, 이것도 useModal 훅?에서는 외부에 해당한다. 그래서 이 토글 버튼을 클릭하면 외부가 클릭 false -> 토글 버튼의 onClick 함수까지 실행 true 두 번 state가 변경돼서 내가 생각한 대로 동작하지 않는다.

그래서 ref를 props로 받아서도 사용해 보고 useContext를 만들어서 App에 적용하고 뭐 이것저것 구글링해서 다 적용해 봤는데 제대로 안 돼서 며칠 쉬었다 오늘 다시 구글링하다 내가 예전에 프로젝트에서 만든 useToggle 커스텀 훅을 발견함

방법은 동일한데 약간의 차이가 있다면,, 부모의? ref를 받고 hook 내부에서 state를 관리해서 내보낸 것을 받아서 사용한다는 것. 설명을 잘 못하겠는 것을 보니 내가 작성하고도 아직 완전 다 이해한 건 아닌 것 같음 ㅜ

// useToggle.ts
import { useEffect, useState } from 'react';

export default function useToggle(toggleRef: React.RefObject<HTMLElement>) {
  const [toggle, setToggle] = useState(false);

  const onToggleChange = () => setToggle((prev) => !prev);

  const clickToggleOutside = (event: any) => {
    if (!toggleRef.current?.contains(event.target)) {
      setToggle(false);
    }
  };

  useEffect(() => {
    if (toggle) {
      document.addEventListener('mousedown', clickToggleOutside);
    }
    return () => {
      document.addEventListener('mousedown', clickToggleOutside);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [toggle]);

  return { toggle, onToggleChange };
}

// Post.tsx
function Post () {
  const toggleRef = useRef() as React.RefObject<HTMLDivElement>;
  const { toggle, onToggleChange } = useToggle(toggleRef);
  ...
  return (
    <>
      ...
      <div ref={toggleRef}>
        <button onClick={onToggleChange}></button>
        {toggle && (
          <div>
            <button>수정하기</button>
            <button>삭제하기</button>
          </div>
        )}
      </div>
    </>
  )
}

clickToggleOutside 함수의 event type을 any가 아닌 정확한 타입으로 수정하고 싶고, 그리고 전체 화면이 리렌더링되는 것도 방지할 수 있으면 해보고 이것저것 시간 내서 개선해야겠다.

참고한 사이트들
[React]리액트 훅, 외부클릭 감지 모달창 구현
Your next React Modal with your own "useModal" Hook & Context API.

2.알고리즘 자릿수의 합 (완탐)
이건 전에 강의 보고 풀었는데 다시 직접 풀었다. 2중 for 문에 배열을 도는 코드가 많아서 효율적이지 못하다 생각하지만 전에도 비슷하게 시도했다 실패했는데 원인이 배열 안에 원소들이 숫자라 length를 읽지 못해서였다. 그래서 strArr란 변수에 문자열로 만들고
const strArr = arr.map(String);
i0일 때 : strArr[0] -> '128' 의 각 자리를 더하기 위해 이것의 length인 3(j)만큼 돌면서 Number(strArr[i][j]) 1 2 8를 숫자로 바꿔 sum에 더하고 i에 해당하는 128(+'128'->128)과 합을 넣은 배열을 push하고 sum을 0으로 초기화하고 반복.. 풀었다는 거에 의의를 둔다,,

function solution(arr) {
  let answer = [];
  const strArr = arr.map(String);

  for (let i = 0; i < strArr.length; i++) {
    let sum = 0;
    for (let j = 0; j < strArr[i].length; j++) {
      sum += Number(strArr[i][j]);
    }
    answer.push([+strArr[i], +sum]);
    sum = 0;
  }
  // answer => [[128, 11],[460, 10],[603, 9],[40, 4],[521, 8],[137, 11],[123, 6]];

  // 정렬
  const sortedArr = answer.sort((a, b) => b[1] - a[1]);
  // sortedArr => [[128, 11],[137, 11],[460, 10],[603, 9],[521, 8],[123, 6],[40, 4]];

  for (let i = 0; i < sortedArr.length; i++) {
    if (sortedArr[i][1] === sortedArr[i + 1][1]) {
      if (sortedArr[i][1] > sortedArr[i + 1][1]) {
        return sortedArr[i][0];
      } else {
        return sortedArr[i + 1][0];
      }
    } else {
      return sortedArr[i][1];
    }
  }
}

console.log(solution([128, 460, 603, 40, 521, 137, 123])); // 137

근데 동시에 답이 될 수 있다면 더 큰 숫자를 답으로 해야 하는데 그걸 마지막에 for 문으로 arr의 i, i+1에 해당하는 원소를 return 하는 방식으로 구현했는데 만약 여러 개라면 답이 안 나올 것 같아서 수정해따

  ...
  const sortedArr = answer
    .sort((a, b) => b[1] - a[1])
    // => [[128, 11],[137, 11],[460, 10],[603, 9],[521, 8],[123, 6],[40, 4]];
    .filter(([_, sum]) => sum === answer[0][1])
    // [ [ 128, 11 ], [ 137, 11 ] ]
    .map(([num, _]) => num); // [ 128, 137 ]

  return Math.max(...sortedArr); // = Math.max(128, 137)
}

console.log(solution([128, 460, 603, 40, 521, 137, 123])); // 137

3.React Router Outlet
중첩 라우터에서 Outlet 컴포넌트를 사용해서 구현

4.useParms type
https://stackoverflow.com/questions/59085911/required-url-param-on-react-router-v5-with-typescript-can-be-undefined

import { useParams } from 'react-router-dom';

interface ParamsType {
  id: string;
}

function App() {
  const { id } = useParams<ParamsType>();
  // or
  const { id } = useParams<{ id: string }>();
}
profile
꾸준히 자유롭게 즐겁게

0개의 댓글