I try to add debounce to handleInputChange in my React component. When user types in textarea, is shouldn't constantly rerender (I check it by console.log(commentBody), but maybe I'am wrong, I'm not very experienced in React) but the problem is that it doesn't work properly. All variants I tried add delay to typing as well, see below.
import { debounce } from "lodash";
import ReviewCard from "../ReviewCard/ReviewCard";
const CommentInput = ({ postId, userId, onCommentChange, onCommentSubmit }) => {
const dispatch = useDispatch();
const [commentBody, setCommentBody] = useState("");
const [isSubmitting, setIsSubmitting] = useState(false);
const [submittedText, setSubmittedText] = useState("");
const [submittedComments, setSubmittedComments] = useState([]);
console.log(commentBody);
const handleInputChange = (e) => {
setCommentBody(e.target.value);
};
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setIsSubmitting(true);
const newComment = await dispatch(
postComment({ body: commentBody, postId, userId })
);
setSubmittedText(commentBody);
setIsSubmitting(false);
onCommentChange("");
onCommentSubmit(newComment.payload);
setSubmittedComments([...submittedComments, newComment]);
setCommentBody("");
};
return (
<div className={st.add_comment}>
{submittedComments.map((comment, index) => (
<ReviewCard
key={index}
reviewerName={`@${comment.payload.user.username}`}
commentary={submittedText}
/>
))}
<Text type={"h3"}>
Add <span className={st.highlight}> comment </span>
</Text>
<form className={st.add_comment_form} onSubmit={handleSubmit}>
<textarea
className={st.add_comment_input}
value={commentBody}
// onChange={(e) => setCommentBody(e.target.value)}
onChange={handleInputChange}
placeholder="ENTER YOUR COMMENT"
disabled={isSubmitting}
/>
<Button type={"primary"} disabled={isSubmitting || !commentBody.trim()}>
{isSubmitting ? "Posting..." : "Send"}
</Button>
</form>
</div>
);
};
export default CommentInput;
I tried to do so:
const debouncedSave = debounce((value) => setCommentBody(value), 300);
const handleInputChange = (e) => {
debouncedSave(e.target.value);
};
but the problem is that it adds delay to typing also which is not what I need.
If you want to have debounce, you have 2 options.
If you want to keep your input controlled (controlling its value with state) You should use a separate state which stores the DEBOUNCED value. It would be like this:
const MyComponent = () => {
const [value, setValue] = useState("")
const [debouncedValue, setDebouncedValue] = useState(value)
const debounceRef = useRef<NodeJS.Timeout>(null);
useEffect(() => {
clearTimeout(debounceRef.current)
debounceRef.current = setTimeout(() => setDebouncedValue(value), 300)
}, [value])
return <textarea value={value} />
}
You can also do this without using 2 states while our input is uncontrolled. Let's look at the example:
const MyComponent = () => {
const [debouncedValue, setDebouncedValue] = useState(value)
const debounceRef = useRef<NodeJS.Timeout>(null);
return (
<textarea
defaultValue="" // you can still use defaultValue if you want (optional)
onChange={(e) => {
clearTimeout(debounceRef.current)
debounceRef.current = setTimeout(() => setDebouncedValue(value), 300)
}}
/>
)
}
Both solutions work but I like the first one as I have more control over the input and I can change the main value anytime I want.
BONUS: You can use @mantine/hooks
library which exposes a log of custom hooks that can help you in such a place. It has 2 hooks that you can use for debouncing: useDebouncedState
and useDebouncedValue
. I use them in my real projects. You can have a look at the examples here: