React

[React]검색어 자동완성과 클릭투에디트를 구현해보자

2023. 4. 20. 16:18
목차
  1. 자동완성
  2. 헷갈렸던 점 1
  3. 헷갈렸던 점 2
  4. 헷갈렸던 점 3
  5. Click To Edit
  6. 헷갈렸던 점 1
  7. 몰랐던 점

자동완성

import { useState, useEffect } from 'react';
import styled from 'styled-components';

const deselectedOptions = [
  'rustic',
  'antique',
  'vinyl',
  'vintage',
  'refurbished',
  '신품',
  '빈티지',
  '중고A급',
  '중고B급',
  '골동품'
];

/* TODO : 아래 CSS를 자유롭게 수정하세요. */
const boxShadow = '0 4px 6px rgb(32 33 36 / 28%)';
const activeBorderRadius = '1rem 1rem 0 0';
const inactiveBorderRadius = '1rem 1rem 1rem 1rem';

export const InputContainer = styled.div`
  margin-top: 8rem;
  background-color: #ffffff;
  display: flex;
  flex-direction: row;
  padding: 1rem;
  border: 1px solid rgb(223, 225, 229);
  border-radius: ${inactiveBorderRadius};
  z-index: 3;
  box-shadow: 0;

  &:focus-within {
    box-shadow: ${boxShadow};
  }

  > input {
    flex: 1 0 0;
    background-color: transparent;
    border: none;
    margin: 0;
    padding: 0;
    outline: none;
    font-size: 16px;
  }

  > div.delete-button {
    cursor: pointer;
  }
`;

export const DropDownContainer = styled.ul`
  background-color: #ffffff;
  display: block;
  margin-left: auto;
  margin-right: auto;
  list-style-type: none;
  margin-block-start: 0;
  margin-block-end: 0;
  margin-inline-start: 0px;
  margin-inline-end: 0px;
  padding-inline-start: 0px;
  margin-top: -1px;
  padding: 0.5rem 0;
  border: 1px solid rgb(223, 225, 229);
  border-radius: 0 0 1rem 1rem;
  box-shadow: ${boxShadow};
  z-index: 3;

  > li {
    padding: 0 1rem;
  }
`;

export const Autocomplete = () => {
  /**
   * Autocomplete 컴포넌트는 아래 3가지 state가 존재합니다. 필요에 따라서 state를 더 만들 수도 있습니다.
   * - hasText state는 input값의 유무를 확인할 수 있습니다.
   * - inputValue state는 input값의 상태를 확인할 수 있습니다.
   * - options state는 input값을 포함하는 autocomplete 추천 항목 리스트를 확인할 수 있습니다.
   */
  const [hasText, setHasText] = useState(false);
  const [inputValue, setInputValue] = useState('');
  const [options, setOptions] = useState(deselectedOptions);

  // useEffect를 아래와 같이 활용할 수도 있습니다. //지웠을때?
  useEffect(() => {
    if (inputValue === '') {
      setHasText(false);
    }
  }, [inputValue]);

  // TODO : input과 dropdown 상태 관리를 위한 handler가 있어야 합니다.
  const handleInputChange = (event) => {
    /**
     * handleInputChange 함수는
     * - input값 변경 시 발생되는 change 이벤트 핸들러입니다.
     * - input값과 상태를 연결시킬 수 있게 controlled component로 만들 수 있고
     * - autocomplete 추천 항목이 dropdown으로 시시각각 변화되어 보여질 수 있도록 상태를 변경합니다.
     *
     * handleInputChange 함수를 완성하여 아래 3가지 기능을 구현합니다.
     *
     * onChange 이벤트 발생 시
     * 1. input값 상태인 inputValue가 적절하게 변경되어야 합니다.
     * 2. input값 유무 상태인 hasText가 적절하게 변경되어야 합니다.
     * 3. autocomplete 추천 항목인 options의 상태가 적절하게 변경되어야 합니다.
     * Tip : options의 상태에 따라 dropdown으로 보여지는 항목이 달라집니다.
     */
    setInputValue(event.target.value);
    setHasText(true);
    setOptions(deselectedOptions.filter(el=>el.includes(event.target.value.toLowerCase())))
    // 예를 들어 문자열 r이 포함된 모든요소 리턴...? 필터?
  };

  const handleDropDownClick = (clickedOption) => {
    /**
     * handleDropDownClick 함수는
     * - autocomplete 추천 항목을 클릭할 때 발생되는 click 이벤트 핸들러입니다.
     * - dropdown에 제시된 항목을 눌렀을 때, input값이 해당 항목의 값으로 변경되는 기능을 수행합니다.
     *
     * handleInputChange 함수를 완성하여 아래 기능을 구현합니다.
     *
     * onClick 이벤트 발생 시
     * 1. input값 상태인 inputValue가 적절하게 변경되어야 합니다.
     * 2. autocomplete 추천 항목인 options의 상태가 적절하게 변경되어야 합니다.
     */
    setInputValue(clickedOption)//클릭된 값
  };

  const handleDeleteButtonClick = () => {
    /**
     * handleDeleteButtonClick 함수는
     * - input의 오른쪽에 있는 X버튼 클릭 시 발생되는 click 이벤트 핸들러입니다.
     * - 함수 작성을 완료하여 input값을 한 번에 삭제하는 기능을 구현합니다.
     *
     * handleDeleteButtonClick 함수를 완성하여 아래 기능을 구현합니다.
     *
     * onClick 이벤트 발생 시
     * 1. input값 상태인 inputValue가 빈 문자열이 되어야 합니다.
     */
    setInputValue('')
  };

  // Advanced Challenge: 상하 화살표 키 입력 시 dropdown 항목을 선택하고, Enter 키 입력 시 input값을 선택된 dropdown 항목의 값으로 변경하는 handleKeyUp 함수를 만들고,
  // 적절한 컴포넌트에 onKeyUp 핸들러를 할당합니다. state가 추가로 필요한지 고민하고, 필요 시 state를 추가하여 제작하세요.

  return (
    <div className='autocomplete-wrapper'>
      <InputContainer>
        {/* TODO : input 엘리먼트를 작성하고 input값(value)을 state와 연결합니다. handleInputChange 함수와 input값 변경 시 호출될 수 있게 연결합니다. */}
        <input 
        onChange={handleInputChange}
        value={inputValue}
        type='text'
        />
        {/* TODO : 아래 div.delete-button 버튼을 누르면 input 값이 삭제되어 dropdown이 없어지는 handler 함수를 작성합니다. */}
        <div onClick={handleDeleteButtonClick} className='delete-button'>&times;</div>
      </InputContainer>
      {/* TODO : input 값이 없으면 dropdown이 보이지 않아야 합니다. 조건부 렌더링을 이용해서 구현하세요. */}
      {hasText ? <DropDown handleDropDownClick={handleDropDownClick} options={options} handleComboBox={handleInputChange} /> : null}
    </div>
  );
};

export const DropDown = ({ options, handleComboBox,handleDropDownClick }) => {
  return (
    <DropDownContainer>
      {/* TODO : input 값에 맞는 autocomplete 선택 옵션이 보여지는 역할을 합니다. */}
      {options.map((el,index)=>{
                    return <li onClick={()=>handleDropDownClick(el)} key={index}>{el}</li>
      })}
    </DropDownContainer>
  );
};

헷갈렸던 점 1

const handleInputChange = (event) => {
    setInputValue(event.target.value);
    setHasText(true);
    setOptions(deselectedOptions.filter(el=>el.includes(event.target.value.toLowerCase())))
  };

setHasText(여기에 무엇을 적어줘야할지 헷갈렸는데)

같은 컴포넌트안에 useEffect훅이 있었다.

  useEffect(() => {
    if (inputValue === '') {
      setHasText(false);
    }
  }, [inputValue]);

input의 value가 변경되었을때 true가 되지만

만약 사용자가 input 에 검색어 입력"후"다 지운다면

그때는 false값이 되어야 한다.

useEffect훅을 사용하지 않았다면, 계속 setHasText는 true값일것이다.

 

 

헷갈렸던 점 2

 setOptions(deselectedOptions.filter(el=>el.includes(event.target.value.toLowerCase())))

이부분도 filter에는 무슨 배열을 걸러야 할까 고민했는데

value가 변할때마다 모든 데이터를 가지고 있는 배열에서 같은 글자를 찾아야하기때문에

deselectedOptions를 쓰는것이 맞다.

 

헷갈렸던 점 3

    <div className='autocomplete-wrapper'> //중략
		{hasText ? <DropDown handleDropDownClick={handleDropDownClick} options={options} handleComboBox={handleInputChange} /> : null}
    </div>
     

 export const DropDown = ({ options, handleComboBox,handleDropDownClick }) => {
  return (
    <DropDownContainer>
      {/* TODO : input 값에 맞는 autocomplete 선택 옵션이 보여지는 역할을 합니다. */}
      {options.map((el,index)=>{
                    return <li onClick={()=>handleDropDownClick(el)} key={index}>{el}</li>
      })}
    </DropDownContainer>
  );
};

props로 넘겨주는 부분이 여전히 헷갈린다!

부모 => 자식 컴포넌트로 전달해줄때

속성값을 적기만 하면되는데

모드가 너무 장황해서 어버버했다.

 

자식컴포넌트에서 필요한 함수도 잘 생각하고

특히!! map 사용시 index 인자로 받아서 key 값 까먹지 않기!

 

Click To Edit

import { useEffect, useState, useRef } from 'react';
import styled from 'styled-components';

export const InputBox = styled.div`
  text-align: center;
  display: inline-block;
  width: 150px;
  height: 30px;
  border: 1px #bbb dashed;
  border-radius: 10px;
  margin-left: 1rem;
`;

export const InputEdit = styled.input`
  text-align: center;
  display: inline-block;
  width: 150px;
  height: 30px;
`;

export const InputView = styled.div`
  text-align: center;
  align-items: center;
  margin-top: 3rem;

  div.view {
    margin-top: 3rem;
  }
`;

export const MyInput = ({ value, handleValueChange }) => {
  const inputEl = useRef(null);
  const [isEditMode, setEditMode] = useState(false);
  const [newValue, setNewValue] = useState(value); 
  //보이게될 값 isEditedMode가 true일때 업데이트

  useEffect(() => {
    if (isEditMode) { // input창을 클릭했을때 true=>포커스메소드실행
      inputEl.current.focus();
    }
  }, [isEditMode]);

  useEffect(() => {
    setNewValue(value);
  }, [value]);

  const handleClick = () => {
    // TODO : isEditMode 상태를 변경합니다.
    setEditMode(true)
  };

  const handleBlur = () => {
    // TODO : Edit가 불가능한 상태로 변경합니다.
    handleValueChange(newValue); 
    //handleValueChange는 ClickToEdit 컴포넌트의 input value를 업데이트한다
    setEditMode(false)
  };

  const handleInputChange = (e) => {
    // TODO : 저장된 value를 업데이트합니다.
    setNewValue(e.target.value) //사용자가 입력한 새로운 value를 상태변경함수에 넣어준다.
    handleValueChange(e.target.value) //새로운 값인 input value를 변경함수에 인자로 전달
  };

  return (
    <InputBox>
      {isEditMode ? (
        <InputEdit
          type='text'
          value={newValue}
          ref={inputEl}
          onBlur={handleBlur}
          onChange={handleInputChange}
          // TODO : 포커스를 잃으면 Edit가 불가능한 상태로 변경되는 메소드가 실행되어야 합니다.
          // TODO : 변경 사항이 감지되면 저장된 value를 업데이트 되는 메소드가 실행되어야 합니다.
        />
      ) : (
        <span onClick={handleClick} // handleClick함수로 editMode 변경
        // TODO : 클릭하면 Edit가 가능한 상태로 변경되어야 합니다.
        >{newValue}</span>
      )}
    </InputBox>
  );
}

const cache = {
  name: '김코딩',
  age: 20
};

export const ClickToEdit = () => {
  const [name, setName] = useState(cache.name);
  const [age, setAge] = useState(cache.age);

  return (
    <>
      <InputView>
        <label>이름</label>
        <MyInput value={name} handleValueChange={(newValue) => setName(newValue)} />
      </InputView>
      <InputView>
        <label>나이</label>
        <MyInput value={age} handleValueChange={(newValue) => setAge(newValue)} />
      </InputView>
      <InputView>
        <div className='view'>이름 {name} 나이 {age}</div>
      </InputView>
    </>
  );
};

헷갈렸던 점 1

일단 전체적으로 코드의 연관성이 헷갈렸다.

 

input창을 클릭했을 때 edit모드가되며

input외의 부분을 클릭했을 때 edit모드가 종료된다.

 

여기서 상태는 edit모드와 input의 value

일어나는 이벤트는 onClick과 onChange이다.

onChang가 일어났을 때 handleInputChange함수를 통해서

input값의 상태를 변경해주는 함수인 setNewValue에 value를인자로

부모컴포넌트의 값을 변경하는 함수인 props로 받아온 handleValueChange에도

역시 value를 인자로 넘겨주어 input에 새로운 값이 렌더링되게 해준다.

 

몰랐던 점

onBlur는 input요소가 포커스를 잃었을때 발생하는 이벤트를 감지하는 메소드이다.

사용자가 특정 input요소를 클릭하고 다른요소를 클릭할때

onBlur이벤트가 발생한다.

'React' 카테고리의 다른 글

[Redux] Redux의 데이터 흐름과 Flux패턴  (3) 2023.04.24
[React] 쇼핑몰앱 장바구니기능을 구현해보자  (2) 2023.04.21
리액트로 모달창,토글,탭,태그 구현  (0) 2023.04.19
useEffect훅을 사용해 과제를 풀어보았다.  (0) 2023.04.03
[React] Effect Hook  (0) 2023.04.01
  1. 자동완성
  2. 헷갈렸던 점 1
  3. 헷갈렸던 점 2
  4. 헷갈렸던 점 3
  5. Click To Edit
  6. 헷갈렸던 점 1
  7. 몰랐던 점
'React' 카테고리의 다른 글
  • [Redux] Redux의 데이터 흐름과 Flux패턴
  • [React] 쇼핑몰앱 장바구니기능을 구현해보자
  • 리액트로 모달창,토글,탭,태그 구현
  • useEffect훅을 사용해 과제를 풀어보았다.
Summer.dev
Summer.dev
프론트엔드 개발자 Summer 입니다! 피드백은 언제나 환영입니다.
Summer.dev
꾸준함이 무기
Summer.dev
전체
오늘
어제
  • 분류 전체보기
    • Projects
      • Next.js board-project
      • MOMO
    • 원티드
    • 우테코 프리코스
    • JavaScript
    • React
    • TypeScript
    • Node.js
    • Algorithm
      • 코플릿
      • 개념정리
    • 네트워크
    • 오류해결
    • 회고
    • 기술면접준비
    • git,github
    • 소소하게 궁금한것
    • Next.js Beta Docs 번역
    • 디자인패턴
    • 트러블슈팅
    • 번역

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 알고리즘
  • 메모이제이션

최근 댓글

최근 글

hELLO · Designed By 정상우.
Summer.dev
[React]검색어 자동완성과 클릭투에디트를 구현해보자
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.