我正在构建一个搜索字段,该字段根据用户输入从数据库中获取,但我遇到了一些困难。目前,它在每次击键时都获取数据,这并不理想。我查看了不同的答案,似乎最好的选择是在 componentDidUpdate() 中执行此操作,并通过 setTimeout() 获取输入感觉的引用以将其与当前值进行比较。
我已经试过了,但我仍然在每次击键时获取数据,不知道为什么?请参阅下面的组件示例:
class ItemsHolder extends Component {
componentDidMount() {
//ensures the page is reloaded at the top when routing
window.scrollTo(0, 0);
this.props.onFetchItems(this.props.search);
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.search !== this.props.search) {
console.log(
this.props.search ===
this.props.searchRef.current.props.value.toUpperCase()
);
setTimeout(() => {
console.log(
this.props.search ===
this.props.searchRef.current.props.value.toUpperCase()
);
if (
this.props.search ===
this.props.searchRef.current.props.value.toUpperCase()
) {
this.props.onFetchItems(this.props.search, this.props.category);
}
}, 500);
}
}
我正在使用 Redux 进行状态管理。这是获取项目时调用的函数:
export const fetchItemsFromServer = (search) => {
return (dispatch) => {
dispatch(fetchItemsStart());
const query =
search.length === 0 ? '' : `?orderBy="country"&equalTo="${search}"`;
axios
.get('/items.json' + query)
.then((res) => {
const fetchedItems = [];
for (let item in res.data) {
fetchedItems.push({
...res.data[item],
id: item,
});
}
dispatch(fetchItemsSuccess(fetchedItems));
})
.catch((error) => {
dispatch(fetchItemsFail(error));
});
};
};
这就是我在搜索组件中设置 ref 的方式:
class Search extends Component {
constructor(props) {
super(props);
this.searchInput = React.createRef();
}
componentDidMount() {
this.props.onSetRef(this.searchInput);
}
render() {
return (
<Input
ref={this.searchInput}
toolbar
elementType={this.props.inputC.elementType}
elementConfig={this.props.inputC.elementConfig}
value={this.props.inputC.value}
changed={(event) => this.props.onChangedHandler(event)}
/>
);
}
}
根据教程,我发现这应该可行。供您引用,请参阅本教程中的代码。我不明白为什么上面的方法不起作用。唯一的区别是教程使用了钩子(Hook)。
const Search = React.memo(props => {
const { onLoadIngredients } = props;
const [enteredFilter, setEnteredFilter] = useState('');
const inputRef = useRef();
useEffect(() => {
const timer = setTimeout(() => {
if (enteredFilter === inputRef.current.value) {
const query =
enteredFilter.length === 0
? ''
: `?orderBy="title"&equalTo="${enteredFilter}"`;
fetch(
'https://react-hooks-update.firebaseio.com/ingredients.json' + query
)
.then(response => response.json())
.then(responseData => {
const loadedIngredients = [];
for (const key in responseData) {
loadedIngredients.push({
id: key,
title: responseData[key].title,
amount: responseData[key].amount
});
}
onLoadIngredients(loadedIngredients);
});
}
}, 500);
return () => {
clearTimeout(timer);
};
}, [enteredFilter, onLoadIngredients, inputRef]);
遵循 debounceInput 的建议:
import React, { Component } from 'react';
// import classes from './Search.css';
import Input from '../../UI/Input/Input';
// redux
import * as actions from '../../../store/actions/index';
import { connect } from 'react-redux';
class Search extends Component {
componentDidUpdate(prevProps, prevState) {
if (prevProps.search !== this.props.search) {
this.props.onFetchItems(this.props.search, this.props.category);
}
}
debounceInput = (fn, delay) => {
let timerId;
return (...args) => {
clearTimeout(timerId);
timerId = setTimeout(() => fn(...args), delay);
};
};
render() {
return (
<Input
toolbar
elementType={this.props.inputC.elementType}
elementConfig={this.props.inputC.elementConfig}
value={this.props.inputC.value}
changed={(event) =>
this.debounceInput(this.props.onChangedHandler(event), 500)
}
/>
);
}
}
const mapStateToProps = (state) => {
return {
inputC: state.filtersR.inputConfig,
search: state.filtersR.search,
};
};
const mapDispatchToProps = (dispatch) => {
return {
onChangedHandler: (event) => dispatch(actions.inputHandler(event)),
onFetchItems: (search, category) =>
dispatch(actions.fetchItemsFromServer(search, category)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Search);
这里是得到帮助后的最终解决方案:
import React, { Component } from 'react';
// import classes from './Search.css';
import Input from '../../UI/Input/Input';
// redux
import * as actions from '../../../store/actions/index';
import { connect } from 'react-redux';
const debounceInput = (fn, delay) => {
let timerId;
return (...args) => {
clearTimeout(timerId);
timerId = setTimeout(() => fn(...args), delay);
};
};
class Search extends Component {
componentDidUpdate(prevProps, _prevState) {
if (prevProps.search !== this.props.search) {
this.responseHandler();
}
}
responseHandler = debounceInput(() => {
this.props.onFetchItems(this.props.search, this.props.category);
}, 1000);
render() {
return (
<Input
toolbar
elementType={this.props.inputC.elementType}
elementConfig={this.props.inputC.elementConfig}
value={this.props.inputC.value}
changed={(event) => this.props.onChangedHandler(event)}
/>
);
}
}
const mapStateToProps = (state) => {
return {
inputC: state.filtersR.inputConfig,
search: state.filtersR.search,
};
};
const mapDispatchToProps = (dispatch) => {
return {
onChangedHandler: (event) => dispatch(actions.inputHandler(event)),
onFetchItems: (search, category) =>
dispatch(actions.fetchItemsFromServer(search, category)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Search);
最佳答案
您真的只需要去抖输入的 onChange
处理程序,或者更好的是,实际执行异步工作的函数。
非常简单的去抖动高阶函数:
const debounce = (fn, delay) => {
let timerId;
return (...args) => {
clearTimeout(timerId);
timerId = setTimeout(() => fn(...args), delay);
}
};
使用示例:
fetchData = debounce(() => fetch(.....).then(....), 500);
componentDidUpdate(.......) {
// input value different, call fetchData
}
<Input
toolbar
elementType={this.props.inputC.elementType}
elementConfig={this.props.inputC.elementConfig}
value={this.props.inputC.value}
changed={this.props.onChangedHandler}
/>
演示代码
const debounce = (fn, delay) => {
let timerId;
return (...args) => {
clearTimeout(timerId);
timerId = setTimeout(fn, delay, [...args]);
};
};
const fetch = (url, options) => {
console.log("Fetching", url);
return new Promise((resolve) => {
setTimeout(() => {
console.log("Fetch Resolved");
resolve(`response - ${Math.floor(Math.random() * 1000)}`);
}, 2000);
});
};
export default class App extends Component {
state = {
search: "",
response: ""
};
changeHandler = (e) => {
const { value } = e.target;
console.log("search", value);
this.setState({ search: value });
};
fetchData = debounce(() => {
const { search } = this.state;
const query = search.length ? `?orderBy="country"&equalTo="${search}"` : "";
fetch(
"https://react-hooks-update.firebaseio.com/ingredients.json" + query
).then((response) => this.setState({ response }));
}, 500);
componentDidUpdate(prevProps, prevState) {
if (prevState.search !== this.state.search) {
if (this.state.response) {
this.setState({ response: "" });
}
this.fetchData();
}
}
render() {
const { response, search } = this.state;
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<label>
Search
<input type="text" value={search} onChange={this.changeHandler} />
</label>
<div>Debounced Response: {response}</div>
</div>
);
}
}
关于javascript - 当用户停止输入搜索框时执行 api 请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66394801/