11.20 항해 69일차 TIL

20672 단어 TILTIL

SearchBar

  • GrabTalk Page 에서 채팅방을 추가 할 시 이름을 검색하는 SearchBar가 필요했다.

메인 페이지에서 사용하는 SearchBar를 재사용 하고 싶었지만.. 아무래도 재사용이 힘들 것 같이 만들어진 컴포넌트 여서 나중에 리팩토링을 하던가 해야 할 것 같았고 그래서 일단은 최대한 재사용이 가능하게 새로운 SearchBar를 만드는 것을 목표로 잡았다.

로직을 짜면서 신경 써야 할 것이 무었인가?

  • 컴포넌트의 재사용이 목적이다. 최대한 범용적으로 사용할 수 있게 만들어야 한다.
  • 검색을 할 리스트를 Props로 받아온다.
  • 아래 뜨는 연관 검색은 GrabTalk Page에선 사용하지 않으므로 Prop으로 사용 여부를 결정한다.
  • 보여지는 결과는 Children으로 넣어준다.

당장 생각나는 것은 위 항목 정도고 로직을 작성 하면서 추가, 수정이 될 것 같다.

검색 로직

  • 입력 된게 없으면 모든 값을 보여준다.
  • 입력이 될 때 마다 유저 이름에 포함되는지를 체크하여 리스트를 업데이트 한다.

아래와 같이 작성해서 일단 검색 기능은 구현이 되었다.

컴포넌트 재사용을 하기 위해서 고민을 해봤는데 이걸 어떻게 빼야하지..

// ChatSearchBar.js
const ChatSearchBar = ({ listIds, children }) => {
  console.log(listIds);
  const [searchValue, setSearchValue] = React.useState('');
  const roomInfo = useSelector((state) => state.chat.noneChatList);
  
  const handleSearchValue = (e) => {
    setSearchValue(e.target.value);
  };

  return (
    <>
      <SearchBarFrom>
        <input type="text" value={searchValue} onChange={handleSearchValue} />
      </SearchBarFrom>
      {listIds.map((id) => {
				// 검색 내용이 없으면 전부 다 return
        if (searchValue === '') {
          return <GrapTalkAddProfile key={id} userId={id} />;
        }
        if (
				// 검색 내용의 맞는 내용을 return
          roomInfo[id].userName
            .toLowerCase()
            .indexOf(searchValue.toLowerCase()) !== -1
        ) {
          return <GrapTalkAddProfile key={id} userId={id} />;
        }
      })}
    </>
  );
};

진짜 단순하게 해결 한 방법으로는 cloneElement를 사용하여 children에 prop을 넘겨 주면 해결이 되긴 했다.

// ChatSearchBar.js
const ChatSearchBar = ({ listIds, children }) => {
  console.log(listIds);
  const [searchValue, setSearchValue] = React.useState('');
  const roomInfo = useSelector((state) => state.chat.noneChatList);
  console.log('검색 =====>', roomInfo);

  const handleSearchValue = (e) => {
    setSearchValue(e.target.value);
  };
  console.log(searchValue);

  return (
    <>
      <SearchBarFrom>
        <input type="text" value={searchValue} onChange={handleSearchValue} />
      </SearchBarFrom>
      {listIds.map((id) => {
        if (searchValue === '') {
          return React.cloneElement(children, {
            userId: id,
            key: id,
          });
        }
        if (
          roomInfo[id].userName
            .toLowerCase()
            .indexOf(searchValue.toLowerCase()) !== -1
        ) {
          return React.cloneElement(children, {
            userId: id,
            key: id,
          });
        }
      })}
    </>
  );
};

// GrabTalkAdd.js
return (
    <div>
      <Header className="header">
        <IconButton onClick={closeList}>
          <Close />
        </IconButton>
      </Header>
      <ChatSearchBar listIds={noneTalkList}>
        <GrapTalkAddProfile />
      </ChatSearchBar>
    </div>
  );

이게 이렇게 해결을 하긴 했는데.. 진짜 아무런 이유도 없이 뭔가 찝찝하다는 느낌을 받았다..

아무래도 cloneElement를 처음 써보기도 했고 이걸 굳이 복사를 해서 쓰는게 맞나 하는 느낌이라고 해야 하나..

처음 컴포넌트를 만들 때는 "검색이 필요한 모든 부분에서 범용적으로 쓸 수 있게 만들어야지!" 하는 생각으로 만들었는데 작성을 할 수록 어떤식으로 검색을 하는지, 그 검색의 결과가 동일한 형식인지를 파악해서 그 부분에 따라 나누는게 오히려 효율적일 것 같다는 생각이 들었다.

물론 내 생각을 누구한테 확실한 증명을 받을 수 있는게 아니니 혼자만의 생각과 결론이지만 잘못 된 부분이 있다면 나중에 또 다시 배우면 된다!

아무튼 결과

  • 검색어 없을 때

  • 검색어 있을 때


검색 결과가 없을 때 메시지를 띄우는 작업을 하는 중 갑자기 위에 작성한 검색기능이 과연 정상적으로 동작하는게 맞는가 라는 생각이 들었다.

map을 돌린다는 것은 조건과 상관 없이 무조건 배열의 length만큼 돌아갈텐데 그럼 저건 내가 원하는 조건을 반환한게 아니고 나머지는 undefined 이기 때문에 그냥 렌더가 안된 것 같아서 로직을 다시 한번 생각해 보았다.

문자열을 바로 체크하는 것이 아닌 배열로 만들어서 그 만들어진 배열을 map으로 돌린다면 조금 더 깔끔하기도 하고 원래 생각하던 범용적인 SearchBar 컴포넌트를 만들수있지 않을까 하는 생각이 들었다.

지금은 너무 늦었으니 내일 해보기 위해 기록 해둔다.

좋은 웹페이지 즐겨찾기