programing

사용자가 입력을 중지한 경우에만 검색을 시작하는 방법

goodjava 2023. 2. 11. 09:23

사용자가 입력을 중지한 경우에만 검색을 시작하는 방법

사용자가 입력을 중지하면 검색을 수행해야 합니다.set Timeout()을 사용해야 하는 것은 알고 있습니다만, Reactjs에서는 동작 방법을 찾을없습니다.사용자가 몇 초간 입력을 정지했을 때(예를 들어 5), 메서드(검색 처리)를 호출하는 방법을 가르쳐 주세요.사용자가 입력을 중단했는지 확인하기 위해 코드를 어디에 써야 할지 모르겠습니다.

import React, {Component, PropTypes} from 'react';

export default class SearchBox extends Component {

    state={
      name:" ",
    }

    changeName = (event) => {
        this.setState({name: event.target.value}); 
    }

    sendToParent = () => {
        this.props.searching(this.state.name);
    }

    render() {
        return (
            <div>
                 <input type="text"  placeholder='Enter name you wish to Search.'  onChange={this.changeName} />

            </div>
        );
    }
}   

사용자가 입력을 중지할 때 sendToParent 메서드를 호출합니다.

useEffect 후크를 사용하여 구현:

function Search() {
  const [searchTerm, setSearchTerm] = useState('')

  useEffect(() => {
    const delayDebounceFn = setTimeout(() => {
      console.log(searchTerm)
      // Send Axios request here
    }, 3000)

    return () => clearTimeout(delayDebounceFn)
  }, [searchTerm])

  return (
    <input
      autoFocus
      type='text'
      autoComplete='off'
      className='live-search-field'
      placeholder='Search here...'
      onChange={(e) => setSearchTerm(e.target.value)}
    />
  )
}

사용할 수 있습니다.setTimeout당신의 코드에 대해 다음과 같이 말합니다.

state = {
    name: '',
    typing: false,
    typingTimeout: 0
}
changeName = (event) => {
    const self = this;

    if (self.state.typingTimeout) {
       clearTimeout(self.state.typingTimeout);
    }

    self.setState({
       name: event.target.value,
       typing: false,
       typingTimeout: setTimeout(function () {
           self.sendToParent(self.state.name);
         }, 5000)
    });
}

또, 바인드 할 필요가 있습니다.changeName핸들러 함수입니다.

constructor(props) {
   super(props);
   this.changeName = this.changeName.bind(this);
}

또 다른 방법은 다음과 같습니다.

class Search extends Component {
  constructor(props){
    super(props);
    this.timeout =  0;
  }

  doSearch(evt){
    var searchText = evt.target.value; // this is the search text
    if(this.timeout) clearTimeout(this.timeout);
    this.timeout = setTimeout(() => {
      //search function
    }, 300);
  }

   render() {
    return (
      <div className="form-group has-feedback">
        <label className="control-label">Any text</label>
        <input ref="searchInput" type="text" onChange={evt => this.doSearch(evt)} />
      </div>
    );
  }
}

라이브러리(use-debounce)는 멋지고 심플합니다.

세우다

yarn add use-debounce

또는

npm i use-debounce --save

문서의 사용 예

import React, { useState } from 'react';
import { useDebounce } from 'use-debounce';

export default function Input() {
  const [text, setText] = useState('Hello');
  const [value] = useDebounce(text, 1000);

  return (
    <div>
      <input
        defaultValue={'Hello'}
        onChange={(e) => {
          setText(e.target.value);
        }}
      />
      <p>Actual value: {text}</p>
      <p>Debounce value: {value}</p>
    </div>
  );
}

지금 이 순간 내가 좋아했던 것들, 미래에는 달라질 수 있어!

  • 셋업과 사용이 용이함
  • 보일러 플레이트 코드 감소
  • 중간 등급(1,000까지) 및 사용량(npm - 20,000 다운로드/주)
  • 타임아웃, MaxWait 및 기타 기능 지원

나는 lodash의 debounce 함수를 사용했다.

onChangeSearchInput = (evt)=> {
    this.debouncedSearch(evt.target.value);
};

debouncedSearch = debounce(function (query) {
    this.setState({query});
}, 1000);

렌더링 메서드의 어딘가에 이 입력 필드가 있습니다.

<input
    type='text'
    onChange={this.onChangeSearchInput}
    className='uk-input'
    placeholder={'search by name or email...'}
   />

이렇게 전체 컴포넌트의 라이프 사이클을 호출하는 상태 파라미터를 갑자기 변경하지 않고 보다 심플하고 깔끔한 방법으로 작업을 수행할 수 있다고 생각합니다.

constructor(props) {
    super(props);

    //Timer
    this.typingTimeout = null;

    //Event
    this.onFieldChange = this.onFieldChange.bind(this);

    //State
    this.state = { searchValue: '' }; 
}   


 /**
 * Called on the change of the textbox.
 * @param  {[Object]} event [Event object.]
 */
onFieldChange(event) {
    // Clears the previously set timer.
    clearTimeout(this.typingTimeout);

    // Reset the timer, to make the http call after 475MS (this.callSearch is a method which will call the search API. Don't forget to bind it in constructor.)
    this.typingTimeout = setTimeout(this.callSearch, 475);

    // Setting value of the search box to a state.
    this.setState({ [event.target.name]: event.target.value });
}


<div className="block-header">
     <input
           type="text"
           name="searchValue"
           value={this.state.searchValue}
           placeholder="User Name or Email"
           onChange={this.onFieldChange}
     />
</div>

나는 이 커스텀 훅을 사용했지만, 그것은 여전히 완벽하게 작동한다.

export function useSearchDebounce(delay = 350) {
  const [search, setSearch] = useState(null);
  const [searchQuery, setSearchQuery] = useState(null);

  useEffect(() => {
    const delayFn = setTimeout(() => setSearch(searchQuery), delay);
    return () => clearTimeout(delayFn);
  }, [searchQuery, delay]);

  return [search, setSearchQuery];
}

다음과 같은 장소에서 사용

const [search, setSearch] = useSearchDebounce();

<input onChange={(e) => setSearch(e.target.value)}/>

리액트 후크 useEffect는 항상 타이머 ID를 반환하고 다음과 같이 타이머를 쉽게 클리어할 수 있기 때문에 setTimeOut 함수를 사용하여 사용할 수 있습니다.

export const Search = () => {
const [term, setTerm] = useState();
const [results, setResult] = useState([]);

useEffect(() => {
    const searchWiki = async () => {
        const { data } = await axios.get('https://en.wikipedia.org/w/api.php', {
            params: {
                srsearch: term,
            },
        });

        setResult(data.query.search);
    };
    const timerId = setTimeout(() => {
        searchWiki();
     // make a request after 1 second since there's no typing 
    }, 1000);

    return () => {
        clearTimeout(timerId);
    };
}, [term]);

커스텀 훅은 어떠세요?

import {useEffect, useRef, useState} from "react";

export default function useSearchInputState(searchHandler) {
  
  // to prevent calling the handler on component mount
  const didMountRef = useRef(false);

  const [searchValue, setSearchValue] = useState(null);

  useEffect(() => {
    let delayDebounceFn;

    if (didMountRef.current) {
      delayDebounceFn = setTimeout(searchHandler, 600)
    } else {
      didMountRef.current = true;
    }

    return () => clearTimeout(delayDebounceFn);
  }, [searchValue]); // eslint-disable-line react-hooks/exhaustive-deps

  return [searchValue, setSearchValue];

}

사용방법:

function MyComponent(props) {

  const [searchValue, setSearchValue] = useSearchInputState(() => {
    resetData(searchValue ?? null, selectedFilterPos); // replace with your code
  });

  return (
    <input className="Search"
           onChange={e => setSearchValue(e?.target?.value ?? null)}
      />
  );
}

lodash로부터의 debounce를 사용하거나 set Timeout을 사용하여 시뮬레이트할 수 있습니다.

import React, {Component, PropTypes} from 'react';

export default class SearchBox extends Component {
    constructor(props){
       super(props);
       this.state={ name:" "}
       this.timeout =  null;

    }

    changeName = (event) => {
        clearTimeout(timeout);
         if(timeout){
           setTimeout((event)=> this.setState({name: event.target.value}), 200)
         }
    }

    sendToParent = () => {
        this.props.searching(this.state.name);
    }

    render() {
        return (
            <div>
                 <input type="text"  placeholder='Enter name you wish to Search.'  onChange={this.changeName} />

            </div>
        );
    }
}

이렇게 나만의 부품을 만들었어요.

import React, { useState, useEffect } from 'react'

const InputDebounce = props => {
  const { onChange, ...otherProps } = props

  const [inputTimeout, setInputTimeout] = useState(null)

  useEffect(() => () => clearTimeout(inputTimeout), [inputTimeout])

  const inputOnChange = value => {
    if (inputTimeout) clearTimeout(inputTimeout)
    setInputTimeout(
      setTimeout(() => {
        if (onChange) onChange(value)
      }, 1000)
    )
  }

  return (
    <input
      {...otherProps}
      onChange={e => inputOnChange(e.target.value)}
    />
  )
}

export default InputDebounce

그리고 이렇게 아무데서나 쓸 수 있죠.

import React from 'react'
import ReactDOM from 'react-dom'

import InputDebounce from './InputDebounce'

const App = () => {
  const usernameOnChange = value => {
    console.log(value)
  }

  return (
    <div>
      <InputDebounce
        type='text'
        name='username'
        placeholder='Username'
        onChange={usernameOnChange}
      />
    </div>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))

리액트 훅의 경우:

먼저 컴포넌트를 정의합니다.

import React, { useEffect, useState } from "react";

const SearchInputText = ({ value, name, placeholder, onChange }) => {
  // state for keepign search text 
  const [searchText, setSearchText] = useState(value);
  // state for keeping the timeout
  const [searchTextTimeout, setSearchTextTimeout] = useState(null);

  // handler for form submit (pressing enter without waiting for setimeout to trigger)
  const handleSubmit = (e) => {
    e.preventDefault();
    // clear timeout as it'll that would be triggered
    if (searchTextTimeout) {
      clearTimeout(searchTextTimeout);
    }
    onChange(searchText);
  };

  // onChange handler
  const handleOnChange = (e) => {
  // cancelling previous timeouts
    if (searchTextTimeout) {
      clearTimeout(searchTextTimeout);
    }
    // first update the input text as user type
    setSearchText(e.target.value);
    // initialize a setimeout by wrapping in our searchTextTimeout so that we can clear it out using clearTimeout
    setSearchTextTimeout(
      setTimeout(() => {
        onChange(searchText);
        // timeout is 2500ms, change it to less or more.
      }, 2500),
    );
  };

  // making sure that we clear the timeout if/when the component unmount
  useEffect(() => {
    return () => clearTimeout(searchTextTimeout);
  }, [searchTextTimeout]);

  return (
    <form onSubmit={handleSubmit}>
      <input
        name={name}
        placeholder={placeholder}
        type="text"
        value={searchText}
        onChange={handleOnChange}
      />
    </form>
  );
};

export default SearchInputText;

사용방법:

const Parent = () => {
  const handleChange = (e) => {
    // your implementation here
  };
  return (
    <div>
      <SortSearchInput name="search" placeholder="Enter Search" onChange={handleChange} />
    </div>
  );
};

아래 코드는 나에게 매우 적합합니다.

const [filter, setFilter] = useState()

useEffect(() => {
    const search = setTimeout(() => {
        getList()
        //Your search query and it will run the function after 3secs from user stops typing
    }, 3000);
    return () => clearTimeout(search)
}, [filter])

HTML을 다음과 같이 추가합니다.

<input type="text" onInput={(e) => setFilter(e.target.value)} value={filter} />

기능 컴포넌트와 useRef 훅을 사용한 접근방식을 다음에 나타냅니다.

import React, { useRef, useEffect } from "react";

function Search() {
  const [searchTerm, setSearchTerm] = React.useState("");

  const inputRef = useRef<any>()
  
  useEffect(() => {
    let timer: NodeJS.Timeout | null = null

    const sendData = () => {
      // If the user keeps on typing then the timeout is cleared and restarted
      if(timer) clearTimeout(timer)

      timer = setTimeout(() => {
        setSearchTerm(inputRef.current.value)
      }, 3000)
    }

    const element = inputRef.current;
    // Set listener and start timeout
    element.addEventListener('keyup', sendData);

    return () => {
      // Remove listener wwhen unmounting
      element.removeEventListener('keyup', sendData);
    };
  }, []);

  return (
    <div>
      <input
        ref={inputRef}
        autoFocus
        type="text"
        autoComplete="off"
        className="live-search-field"
        placeholder="Search here..."
        
      />
      <p>searchTerm: {searchTerm}</p>
    </div>
  );
}

export default Search;

이 접근방식은 불필요한 재렌더를 회피하고 사용자가 입력을 정지했을 때 이벤트청취자를 사용하여 검색 제출을 처리합니다.

여기 작업 컴포넌트 템플릿이 있습니다.시작에 도움이 되는 파라미터가 포함되어 있습니다.

import React, { Component } from 'react'

const initialState = { results: [], value: '' }

export default class SearchBox extends Component {
  state = initialState
  timeout = null
  search_url = "https://example.com/search?q="
  min_query_length = 2
  timeout_duration = 300

  handleSearchChange = (e) => {
    let value = e.target.value
    clearTimeout(this.timeout);
    if (value.length < 1) {
        return this.setState(initialState) 
    } else {
        this.setState({ value })
        if (value.length>=this.min_query_length) {    
            this.timeout = setTimeout(this.search, this.timeout_duration);
        }
    }
  }

  search = () => {
    // assuming your results are returned as JSON
    fetch(`${this.search_url}${this.state.value}`)
    .then(res => res.json())
    .then(data => {
        this.setState({
            results: data,
        })
    })
  }

  render() {
    return (
          <input
            onChange={this.handleSearchChange}
          />
    )
  }
}

리액트 훅을 사용하여 @anoNewb의 답변에서 수정했습니다.추가 시:

  • 타이머가 아직 실행 중일 때 여러 트리거 방지
  • Add on Form Submit 이벤트

코드 상자

    import React, { useState, useEffect } from "react";

    export default function App() {
      const [search, setSearch] = useState("");
      const [searchTimeout, setSearchTimeout] = useState(null);

      useEffect(() => {
        if (searchTimeout) {
          clearTimeout(searchTimeout);
        }

        setSearchTimeout(
          setTimeout(() => {
            loadUsers();
          }, 1000),
        );

        return () => clearTimeout(searchTimeout);
      }, [search]);

      const loadUsers = () => {
        console.log("axios call with query: ", search);
      };

      return (
        <div className="App">
          <form
            onSubmit={(e) => {
              e.preventDefault();
              if (searchTimeout) {
                clearTimeout(searchTimeout);
              }
              loadUsers();
            }}
          >
            <input
              onChange={(e) => {
                setSearch(e.target.value);
              }}
            />
          </form>
        </div>
      );
    }

아래 코드가 도움이 됩니다.

const[isReady, setReady]  = useState(true);
const onSearchSet =(event:React.ChangeEvent<HTMLInputElement>) => { 

    setCriteria(event.target.value);
    if(isReady) {
        setReady(false);
        const delayDebounceFn = setTimeout(() => {
            // Send Axios request here
            
            props.returnCall(props.RDropID, sortCriteria, event.target.value);

            setReady(true);
          }, 1000)
        
    }
      
};

이 코드를 사가에서도 사용할 수 있습니까?최신 요청을 보내는 데 도움이 됩니다.설정된 타임아웃 시간은 변경할 수 있습니다.저 같은 경우에는 600ms를 썼어요.

  const dispatch = useDispatch();
  const [searchText, setSearchText] = useState('');

  useEffect(() => {
    const sendSearchRequest = setTimeout(() => {
      if (searchText && searchText.length > 2) {
        dispatch(sendRequestToSaga(searchText));
      }
    }, 600);
    return () => clearTimeout(sendSearchRequest);
  }, [searchText]);

useEffect를 사용하면 이 작업이 훨씬 쉬워지고 라이브러리가 필요 없습니다.

import React, { useEffect, useState } from 'react'
import ReactDOM from 'react-dom'

const FuncDemo = () => {
  const [searchStr, setSearchStr] = useState('')

  useEffect(() => {
   const makeApiCall = async () => {
   try {
      // your axios call
    } catch (e) {
    
    }
   }

    const triggerCall = setTimeout(() => {
      makeApiCall()
    }, 500)

    return () => clearTimeout(triggerCall)
  }, [searchStr])


  return (
    <input 
      name='search'
      onChange={e => setSearchString(e.target.value)}
    />
  )
}

ReactDOM.render(<FuncDemo/>, document.getElementById('root'))
function debounce(func, timeout = 300){
 let timer;
 return (...args) => {
   clearTimeout(timer);
   timer = setTimeout(() => { func.apply(this, args); }, timeout);
 };
}

function search(){
 console.log('search');
}

const processChange = debounce(() => search());

입력에 사용할 수 있습니다.

<input type="text" onkeyup="processChange()" />

사용자 lodash javascript 라이브러리 및 사용[_.debounce][1]

changeName: _.debounce(function (val) {
  console.log(val)                
}, 1000)

자동 검색 라이브러리 문제 https://twitter.github.io/typeahead.js/

이 케이스는 간단하기 때문에 빠르고 더러운 솔루션을 사용할 수 있습니다.

onChange: (event) ->
  if @_timeoutTask?
    clearTimeout @_timeoutTask

  @_timeoutTask = setTimeout (=>
    @sendToParent event.target.value
    clearTimeout @_timeoutTask
  ), 5000

이렇게 하면 입력 이벤트 후 5초 후에 작업이 트리거됩니다.새 이벤트가 발생하면 이전 작업이 취소되고 새 작업이 예약되므로 5초 더 기다려야 합니다.

는 React와 같은 연산 입니다._timeoutTask상태 파일 범위, 컴포넌트 상태 또는 컴포넌트 인스턴스.

★★_timeoutTask컴포넌트 레벨로 글로벌하게 저장해야 합니다.렌더링에 영향을 주지 않기 때문에 컴포넌트 상태에서도 영향을 받지 않습니다.따라서 컴포넌트 인스턴스에 직접 연결할 것을 권장합니다.

언급URL : https://stackoverflow.com/questions/42217121/how-to-start-search-only-when-user-stops-typing