skip to Main Content

I have a react component with four ref objects as below.

  const lastNameRef = useRef<HTMLInputElement>(null);
  const firstNameRef = useRef<HTMLInputElement>(null);
  const emailRef = useRef<HTMLInputElement>(null);
  const passwordRef = useRef<HTMLInputElement>(null);

i also have a different button component that takes in a disabled flag as a prop.

<SignInOrSignUpButton
  disabled={false}
  icon={faGear}
  isLoading={isLoading}
  onClick={handleSignUp}
  text="Create Account"
/>

I’m trying to disable the button when any of my inputs are empty. Here is what i tried first
I tried checking if any of the inputs values length is 0 implying the input is empty. This didn’t work

<SignInOrSignUpButton
  disabled={
    lastNameRef.current?.value.length === 0 ||
    firstNameRef.current?.value.length === 0 ||
    emailRef.current?.value.length === 0 ||
    passwordRef.current?.value.length === 0
  }
  icon={faGear}
  isLoading={isLoading}
  onClick={handleSignUp}
  text="Create Account"
/>

I also tried the useState useEffect combination but that also didn’t work.

  useEffect(() => {
    if (
      lastNameRef.current?.value.length === 0 ||
      firstNameRef.current?.value.length === 0 ||
      emailRef.current?.value.length === 0 ||
      passwordRef.current?.value.length === 0
    ) {
      setIsButtonDisabled(true);
    } else {
      setIsButtonDisabled(false);
    }
  }, [
    lastNameRef.current?.value,
    firstNameRef.current?.value,
    emailRef.current?.value,
    passwordRef.current?.value,
  ]);


<SignInOrSignUpButton
  disabled={isButtonDisabled}
  icon={faGear}
  isLoading={isLoading}
  onClick={handleSignUp}
  text="Create Account"
/>

Please help.

2

Answers


  1. Your code doesn’t work because React doesn’t re-render your component. useRef is used as a plain javascript object that persists between renders, but changing it doesn’t cause a re-render as modifying any nested properties.

    I would go with simple states to achieve whatever you are trying, converting the inputs from uncontrolled to controlled input.
    Here are the official React docs about this topic.

    Login or Signup to reply.
  2. The issue here is that updating refs doesn’t cause the component to re-render and the three different approaches you took would require the component to re-render in order to display the button as disabled.

    Although the docs recommend using refs just as an "escape hatch", if you are forced to keep using refs instead of state. Add another ref for the button.

    const buttonRef = useRef(null); 
    

    Create a function to change the disabled attribute for the button:

    const enableButton = () => {
      if (inputRef?.current?.value.length === 0) {
        if (buttonRef.current) {
          buttonRef.current.disabled = true;
        }
      } else {
        if (buttonRef.current) {
          buttonRef.current.disabled = false;
        }
      }
    };
    

    So each <input> will update the buttonRef when there’s a change. Example:

    <input type="text" ref={THE_INPUT_REF} onChange={() => enableButton()}/>
    

    Create a function to return true/false to set the initial disabled value for the button

    const isInitiallyDisabled = () => {
        if (inputRef?.current?.value.length > 0) {
          return false;
        } else {
          return true;
        }
      };
    

    Pass the buttonRef ref to your button component. Note: since you’re using your own function component you will have to use forwardRef within the component to pass the buttonRef

    <SignInOrSignUpButton
      ref={buttonRef}
      disabled={isInitiallyDisabled()}
      icon={faGear}
      isLoading={isLoading}
      onClick={handleSignUp}
      text="Create Account"
    />
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search