這次在寫input search的功能時,使用者輸入的內容,會逐字去打API搜尋,如果每打一個字,API就要搜尋一次,會造成很大的效能問題,加上我這次搭配vue-infinite-loading,直接讓vue-infinite-loading進到一個endless loop,讓瀏覽器當機。
<template>
<input :value=value @input="inputChange"
</template>
<script>
data(){
return{
value: "",
}
},
method:{
inputChange(value) {
console.log(value)
},
}
</script>
每打一個字就會console一次,我想到等使用者一次打了很多字,之後等了幾秒,不再輸入了才執行function,這次是用lodash這套件的功能,直接輕鬆做debounce
Vue
<template>
<input :value=value @input="inputChange"
</template>
<script>
const _ = require("lodash");
export default {
data(){
return{
value: "",
}
},
method:{
inputChange(value) {
this.onSearch(value);
},
onSearch: _.debounce(function () {
console.log(value)
}, 500),
}
}
</script>
這邊設定間隔0.5秒使用者沒有繼續輸入,才會console出value。
react
剛好也看到react設計debounce的作法,也順道記錄下來。
import React, { useState, useEffect, useReducer } from 'react';
import Card from '../UI/Card/Card';
import classes from './Login.module.css';
import Button from '../UI/Button/Button';
const emailReducer = (state, action)=>{
if(action.type === "USER_INPUT") {
return {value:action.val, isValid: action.val.includes('@')}
}
if(action.type === "INPUT_BLUR") {
return {value: state.value, isValid: state.value.includes('@')}
}
return {value:'', isValid: false}
}
const passwordReducer = (state, action)=>{
if(action.type === "USER_INPUT") {
return {value:action.val, isValid: action.val.trim().length > 6}
}
if(action.type === "INPUT_BLUR") {
return {value: state.value, isValid: state.value.trim().length > 6}
}
return {value:'', isValid: false}
}
const Login = (props) => {
// const [enteredEmail, setEnteredEmail] = useState('');
// const [emailIsValid, setEmailIsValid] = useState();
// const [enteredPassword, setEnteredPassword] = useState('');
// const [passwordIsValid, setPasswordIsValid] = useState();
const [formIsValid, setFormIsValid] = useState(false);
const [emailState, dispatchEmail] = useReducer(emailReducer,{
value:'',
isValid: null
})
const [passwordState, dispatchPassword] = useReducer(passwordReducer,{
value:'',
isValid: null
})
const {isValid: emailIsValid} = emailState
const {isValid: passwordIsValid} = passwordState
useEffect(()=>{
const identifier = setTimeout(()=>{
console.log('checking')
setFormIsValid(
emailIsValid && passwordIsValid
);
}, 500)
return ()=>{
console.log('CLEANUP')
clearTimeout(identifier)
}
},[emailIsValid, passwordIsValid])
const emailChangeHandler = (event) => {
dispatchEmail({type:'USER_INPUT', val:event.target.value})
// setEnteredEmail(event.target.value);
};
const passwordChangeHandler = (event) => {
dispatchPassword({type:'USER_INPUT', val:event.target.value})
setFormIsValid(
emailState.isValid && passwordState.isValid
);
};
const validateEmailHandler = () => {
dispatchEmail({type:'INPUT_BLUR'})
// setEmailIsValid(emailState.isValid);
};
const validatePasswordHandler = () => {
dispatchPassword({type:'INPUT_BLUR'})
};
const submitHandler = (event) => {
event.preventDefault();
props.onLogin(emailState.value, passwordState.value);
};
return (
<Card className={classes.login}>
<form onSubmit={submitHandler}>
<div
className={`${classes.control} ${
emailState.isValid === false ? classes.invalid : ''
}`}
>
<label htmlFor="email">E-Mail</label>
<input
type="email"
id="email"
value={emailState.value}
onChange={emailChangeHandler}
onBlur={validateEmailHandler}
/>
</div>
<div
className={`${classes.control} ${
passwordState.isValid === false ? classes.invalid : ''
}`}
>
<label htmlFor="password">Password</label>
<input
type="password"
id="password"
value={passwordState.value}
onChange={passwordChangeHandler}
onBlur={validatePasswordHandler}
/>
</div>
<div className={classes.actions}>
<Button type="submit" className={classes.btn} disabled={!formIsValid}>
Login
</Button>
</div>
</form>
</Card>
);
};
export default Login;