0
\$\begingroup\$

I need people who have experience with React to review my code.

I want to make a debounce using Lodash.

import debounce from 'lodash/debounce'
const [name, setName] = React.useState<string | undefined>(undefined)
const [displayedName, setDisplayedName] = React.useState<string | undefined>(undefined)

const changeName = debounce((e: domProperty) => {
  setName(e.target.value)
}, 1000)

React.useEffect(() => {
  // call API
}, [name])
return (
  <Input
    placeholder="Player name"
    prefix={<UserOutlined />}
    value={displayedName}
    onChange={(e) => {
      changeName(e);
      setDisplayedName(e.target.value);
    }}
  />
)

Is it good? or is there anything wrong there?

So here I use displayedName because when I type something in Input, the name state doesn't change as it might be blocked by a debounce.

For example: when I type "alex" at the beginning (it works), then when I want to add the letter "i" (it doesnt, so I can't add or remove any letter it always "alex"), the name state is still the same as "alex". Therefore I add displayedName.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$
  1. Code you provided can't work properly in some cases. Thing is that debounce returns a completely new function with a separate queue on each new render.
    • So, in case if rerender happens, changeName will be completely new & will apply changes immediately
    • There is an even worse possibility. As the old debounced function is not destroyed, it can overwrite the value written by the new changeName.
    • In order to fix that, you need to wrap it into React.useMemo. Like React.useMemo(debounce(e => { … }, 1000), [])
  2. Consider using a special hook https://usehooks.com/useDebounce/

useDebounce

I will take code here of useDebounce function from cited article:

function useDebounce(value, delay) {
    const [debouncedValue, setDebouncedValue] = useState(value);

    useEffect(
        () => {
            const handler = setTimeout(() => setDebouncedValue(value), delay);

            return () => clearTimeout(handler);
        },
        [value, delay]
    );
    
    return debouncedValue;
}

And in your code usage is going to look like:

const [name, setName] = React.useState<string | undefined>(undefined);
const debouncedName = useDebounce(name, 1000);

const changeName = ((e: domProperty) => {
    setName(e.target.value)
};

React.useEffect(() => {
    // call API
}, [debouncedName])

return (
    <Input
        placeholder="Player name"
        prefix={<UserOutlined />}
        value={displayedName}
        onChange={(e) => {
            changeName(e);
        }}
    />
)

This is more beautiful because you don't need to write the value into two state variables.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.