맞춤형 갈고리를 통해 자동 완성

37650 단어
Autocomplete는 사용자가 현재 입력한 내용에 따라 조언을 제공하는 소프트웨어 기능이다.검색엔진에서 인터넷 상점까지 이 기능은 광범위하게 사용된다.
본고는 ReactJS 맞춤형 갈고리를 사용하여 국가 검색 입력에 간단한 자동 완성을 사용하는 방법을 보여 준다.

사용자 인터페이스 구조


사용자가 국가 이름을 검색할 수 있도록 돕기 위한 입력이 있습니다.사용자의 입력에 따라 우리는 몇 가지 건의를 보여 주고 싶다.

검색 양식의 코드 구조와 스타일은 다음과 같습니다.
// src/autocomplete/index.js

import styled from 'styled-components';
import { ReactComponent as SearchIcon } from './search-icon.svg';
import countries from './countries';

const SearchForm = () => {

  const handleSubmit = (e) => {
    e.preventDefault();
  };

  return (
    <div>
      <Form 
        autoComplete="off" // disable the browser built-in autocomplete
        onSubmit={handleSubmit}
       >
        <Container>
          <Input/>
          <SubmitButton>
            <SearchIcon />
          </SubmitButton>
        </Container>
      </Form>
    </div>
  );
};

const Container = styled.div`
  position: relative;
`;

const List = styled.div`
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  height: 40vh;
  background-color: #293241;
  border-radius: 0 0 5px 5px;
  overflow-y: scroll;
`;

const Item = styled.div`
  border-left: 3px solid ${({ active }) => (active ? 'blue' : 'initial')};
  background-color: ${({ active }) => (active ? 'gray' : 'initial')};
  padding: 0 15px;
  color: ${({ active }) => (active ? 'white' : '#f2e9e4')};
  cursor: pointer;

  &:hover {
    background-color: gray;
  }
`;

const Input = styled.input`
  background-color: #293241;
  color: #f2e9e4;
  border: none;
  width: 400px;
  padding: 15px 15px;
  border-radius: 5px 0 0 0;

  &:focus {
    outline: none;
  }
`;

const Match = styled.strong`
  color: #c9ada7;
`;

const Form = styled.form`
  width: 100vw;
  height: 100vh;
  background-color: #4a4e69;
  padding-top: 10%;
  display: flex;
  align-items: flex-start;
  justify-content: center;
`;

const SubmitButton = styled.button`
  outline: none;
  background-color: #293241;
  padding: 15px 20px;
  border: none;
  color: #f2e9e4;
  border-radius: 0 5px 0 0;

  &:focus {
    outline: none;
  }
`;

export default SearchForm;

자동 완성 갈고리 사용

useAutocomplete는 논리를 자동으로 완성하는 사용자 정의 갈고리입니다.우선, 검색 입력 값의 변화를 처리합니다.
// src/autocomplete/useAutocomplete.js

import { useState } from 'react';

const INITIAL_STATE = {
  value: '',
};

const useAutocomplete = (initialState = INITIAL_STATE) => {
  const [{ value }, setState] = useState(initialState);

  const handleValueChange = (newValue) => {
    setState((s) => ({
      ...s,
      value: newValue,
    }));
  };

  return {
    value,
    handleValueChange,
  };
};

export default useAutocomplete;
이제 검색 입력에 사용할 항목:
// src/autocomplete/index.js

//...
import useAutocomplete from './useAutocomplete';

const SearchForm = () => {
  const { value, handleValueChange } = useAutocomplete();
 //...
  return (
    <div>
          //...
          <Input
            value={value}
            onChange={(e) => handleValueChange(e.target.value)}
          />
         //...
    </div>
  );
};
그 다음으로 우리는 국가 이름을 필터한다.우리는 국가 목록을 handleValueChange 함수의 매개 변수로 얻었다.필터 결과, 즉 갈고리 상태에 저장하는 것이 좋습니다.
// src/autocomplete/useAutocomplete.js

// ...
const INITIAL_STATE = {
  // ...
  suggestions: [], // contains autocomplete suggestions
};

const useAutocomplete = (initialState = INITIAL_STATE) => {
  const [{ value }, setState] = useState(initialState);

  const handleValueChange = (newValue, items) => {
    setState((s) => ({
      ...s,
      value: newValue,
      suggestions: items
        .filter((item) => {
          /*
            if the letters typed by the user match the beginning
            of the item we keep it
          */
          const match = item.substr(0, newValue.length);
          return match && match.toLowerCase() === newValue.toLowerCase();
        })
        .slice(0, 13), // limits the suggestion to 13 items
    }));
  };

  return {
    // ...
    suggestions, // exports the suggestions
  };
};
// ...
사용자가 입력할 때, 우리는 일치하는 부분의 스타일이 모든 제안된 나머지 부분과 다르기를 희망합니다.이를 위해 제안된 데이터 구조 변경은 다음과 같습니다.
// src/autocomplete/useAutocomplete.js

// ...
  const handleValueChange = (newValue, items) => {
    setState((s) => ({
      // ...
      suggestions: items
        .filter((item) => {
          // ...
        })
        .map((item) => ({ rest: item.substr(newValue.length), itemValue: item }))
        .slice(0, 13),
        // ...
handleValueChange의 호출자는 사용자 정의 선별 함수를 제공할 수 있습니다.개별 함수에서 현재 필터 로직을 추출하고 사용자 정의 필터 함수를 옵션으로 적용합니다.
// src/autocomplete/useAutocomplete.js

// ...

const defaultFilter = (inputValue, items) =>
  items
    .filter((item) => {
      const match = item.substr(0, inputValue.length);
      return match && match.toLowerCase() === inputValue.toLowerCase();
    })
    .map((item) => ({
      rest: item.substr(inputValue.length),
      itemValue: item,
    }))
    .slice(0, 13);

const useAutocomplete = () => {
  // ...

  const handleValueChange = (newValue, items, { customFilter } = {}) => {
    // uses customFilter if defined
    const filter = customFilter ?? defaultFilter;

    setState((s) => ({
      // ...
      suggestions: filter(newValue, items),
    }));
  };
//...
이제 다음과 같은 권장 사항을 사용자에게 제시할 수 있습니다.
// src/autocomplete/index.js

// ...
const SearchForm = () => {
  const { value, handleValueChange, suggestions } = useAutocomplete()
  const hasSuggestions = !(Array.isArray(suggestions) && !suggestions.length);

  // ...
          <Input
            value={value}
            onChange={(e) => handleValueChange(e.target.value, data)}
          />
          <SubmitButton>
            <SearchIcon />
          </SubmitButton>
          {hasSuggestions && (
            <List>
              {suggestions.map(({ rest, itemValue }, i) => (
                <Item key={itemValue}>
                  <Match>{value}</Match>
                  {rest}
                </Item>
              ))}
            </List>
          )}
// ...

다음 단계에서 우리가 해야 할 일은 그것을 눌렀을 때 그 중 하나를 선택하는 것이다.select 갈고리에 useAutocomplete 함수를 만듭니다.
// src/autocomplete/useAutocomplete.js

// ...
const useAutocomplete = (initialState = INITIAL_STATE) => {
  // ...
  const select = (value) => setState({ ...INITIAL_STATE, value });

  return {
    // ...
    select,
  };
};
// ...
다음은 각 제안된 항목에 대한 클릭 이벤트를 관리합니다.
// src/autocomplete/index.js

// ...
const SearchForm = () => {
  // imports the select function
  const { ..., select } = useAutocomplete();
  // ...
           {suggestions.map(({ rest, itemValue }, i) => (
             <Item key={itemValue} onClick={() => select(itemValue)}>
// ...
자동 완성 입력은 일반적으로 위쪽과 아래쪽 화살표 키를 사용하여 제안 항목을 탐색할 수 있습니다.또한 [amp]lt;Enter[amp]gt;키를 눌러 권장 사항을 선택할 수도 있습니다.우리는 이 모든 기능을 검색 입력에 추가할 것이다.
제안 간에 이동할 때 현재 갈고리 상태에 있는 권장 사항을 추적해야 합니다.
// src/autocomplete/useAutocomplete.js

const INITIAL_STATE = {
 // ...
  currentFocus: null,
};
처음에 currentFocusnull로 설정되었습니다.초점이 전혀 없다는 뜻이다.사용자가 입력을 시작할 때, 우리는 currentFocus -1 를 원한다.이런 상황에서 중점은 입력 자체다.
왜 우리는 텍스트 입력을 초점을 맞출 수 있는 요소로 간주해야 합니까?이해하기 위해서 구체적인 사례를 생각해 봅시다.사용자가 타자를 시작하고 맨 밑에 있는 제안이 사용자가 찾고 있는 내용과 일치한다고 가정하십시오.사용자가 위쪽 화살표 키를 눌렀을 때, 우리는 초점이 마지막 제안으로 바로 넘어가기를 희망합니다.null-1를 제외하고currentFocus는 주동적으로 건의한 색인을 가지고 있다.

이 점을 고려하여 다음과 같이 함수handleValueChange가 업데이트됩니다.
// src/autocomplete/useAutocomplete.js

// ...
const useAutocomplete = (initialState = INITIAL_STATE) => {
// ...
  const handleValueChange = (newValue, items) => {
    setState((s) => ({
      ...s,
      currentFocus: -1, // the focus is on the search input at the beginning
      value: newValue,
      // ...
그런 다음 세 가지 유형의 키 닫기 이벤트를 처리합니다.
  • 위쪽 화살표
  • 화살표 아래로
  • 입력
  • // src/autocomplete/useAutocomplete.js
    
    // ...
    const useAutocomplete = (initialState = INITIAL_STATE) => {
      const [{ value, suggestions, currentFocus }, setState] = useState(initialState);
      // ...
      const handleKeyDown = ({ key }) => {
        switch (key) {
          case 'ArrowDown':
            setState((s) => ({
              ...s,
              currentFocus:
                s.currentFocus === s.suggestions.length - 1
                  ? 0
                  : s.currentFocus + 1,
            }));
            break;
          case 'ArrowUp':
            setState((s) => ({
              ...s,
              currentFocus:
                s.currentFocus === 0 || s.currentFocus === -1
                  ? s.suggestions.length - 1
                  : s.currentFocus - 1,
            }));
            break;
          case 'Enter':
            if (currentFocus !== null && currentFocus !== -1) {
              select(suggestions[currentFocus].itemValue);
            }
            break;
          default:
            break;
        }
      };
    
      return {
       // ...
       handleKeyDown,
       currentFocus,
      };
    };
    // ...
    
    ArrowDown 사건에 대해 우리는 활동 건의가 맨 밑에 있는지 검사한다: s.currentFocus === s.result.length - 1.만약 이런 상황이라면, 우리는 색인의 맨 끝 제안 0 으로 이동하고, 그렇지 않으면 다음 제안 s.currentFocus + 1 으로 이동합니다.ArrowUp 사건에 대해 과정은 유사하지만 반대이다.만약 현재의 중점이 첫 번째 건의(s.currentFocus === 0)나 입력 자체(s.currentFocus === -1))라면 우리는 최하층s.suggestions.length - 1부터 시작하기를 희망한다.만약 없다면, 우리는 앞의 건의를 보겠다(s.currentFocus - 1.
    다음은 현재 이벤트에 대한 권장 값Enter을 선택하여 이벤트를 처리합니다.
    다음 항목은 검색 입력에 다음 키 처리 프로그램을 추가합니다.
    // src/autocomplete/index.js
    
    <Input
      onKeyDown={handleKeyDown}
      // ...
    />
    
    스타일링 구성 요소
    select(suggestions[currentFocus].itemValue)
    active 속성에 따라 사전 예방적으로 권장되는 디스플레이 방법은 다음과 같습니다.
    // src/autocomplete/index.js
    
    //...
    {result.map(({ match, rest, value }, i) => (
      <Item
        active={i === currentFocus}
        // ...
      >
    // ...
    
    우리의 간단한 자동 완성 입력은 이미 완성되었다.

    The complete code can be found in this repo


    결론

    Item 갈고리는 자동 완성 논리를 추상화했다.그것은 끊임없이 변화하는 입력 값을 제어한 후에 몇 가지 건의를 제기했다.이러한 권장 사항은 기본 필터 또는 사용자 정의 기능을 사용하여 생성됩니다.이 밖에useAutocomplete는 일부 버튼 닫기 이벤트를 처리했습니다.
    그래서 우리는 깨끗한 useAutocomplete 구성 요소를 가지고 코드의 중용성을 얻었다.
    읽어주셔서 감사합니다!

    좋은 웹페이지 즐겨찾기