사용자가 입력을 중지한 경우에만 검색을 시작하는 방법
사용자가 입력을 중지하면 검색을 수행해야 합니다.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
'programing' 카테고리의 다른 글
| NextJs에서 캐시를 지우거나 삭제하는 방법 (0) | 2023.02.11 |
|---|---|
| npm을 사용하여 ES6에서 moment.js를 Import하는 방법 (0) | 2023.02.11 |
| 시스템을 캐스트 또는 변환할 수 없습니다.String to Class 객체 (0) | 2023.02.11 |
| WooCommerce에서 관련 제품 가져오기 및 표시 (0) | 2023.02.11 |
| AngularJS Batarang - interceptedExpressions란 무엇입니까? (0) | 2023.02.11 |