0

I've only now noticed this but for some reason I can't seem to type into some input fields in my page built with React and NextJS. This only happens on mobile, on Desktop the forms works perfectly.

Here's what's weird however. I can type passwords in just fine, this only happens on other input types (such as type="text" and type="email").

Things can be still typed into the text box with keyboard predictions, but not with the normal keyboard.

Here's my form:

function LoginForm(props) {
  return (
    <form onSubmit={props.submitLogin} className={"flex flex-col self-center justify-between w-full"}>
      <div className={"text-xl text-center mb-4"}>Sign In</div>
      <div className={"text-gray-600 text-xs pl-2"}>Username:</div>
      <input
        value={props.formData.username}
        name="username"
        type="text"
        onChange={props.updateField}
        className={"form__username my-1 p-2 rounded border-gray-300 border border-solid"}
      />
      <div className={"text-gray-600 text-xs pl-2"}>Password:</div>
      <input
        value={props.formData.password}
        name="password"
        type="password"
        onChange={props.updateField}
        className={"form__password my-1 p-2 rounded border-gray-300 border border-solid"}
      />
      <button
        className={"bg-blue-500 hover:bg-blue-400 focus:bg-blue-600 focus:shadow-outline p-2 mt-4 rounded border-blue-500 uppercase font-semibold text-gray-100 border border-solid"}
      >Login</button>

      <div className={"p-2 text-center"} dangerouslySetInnerHTML={createMarkup(props.formMessage)}>
      </div>

    </form>
  )
}

The containing Component:

export function LoginModal(p) {
  const [showLogin, toggleLogin] = useState(p.showLogin)

  useEffect(() => {
    toggleLogin(p.showLogin);
  }, [p]);

  const sendSignupForm = e => {
    e.preventDefault();
    if ( Object.values(form).some(empty) ) {
      props.setMessage("Please fill all the fields.")
    } else {
      props.setSignupData(form)
    }
  };

  const [form, setValues] = useState({
    username: '',
    password: '',
    checked: false,
    email: ''
  });

  const [message, setMessage] = useState('')

  const updateField = e => {
    setValues({
      ...form,
      [e.target.name]: e.target.type === 'checkbox' ? e.target.checked : e.target.value
    });
  };

  const spring = useSpring({
    from: { opacity: 0 },
    to: { opacity: !p.state ? 1 : 0 }
  });

  const transitions = useTransition(!p.state, null, {
    from: { transform: `scale(1.5)`, opacity: 0 },
    enter: { transform: `scale(1)`, opacity: 1 },
    leave: { transform: `scale(1.5)`, opacity: 0 },
    unique: true
  });

  const submitLogin = async(e) => {
    e.preventDefault();
    await
      login(form).then(data => {
        if(data.message) {
          setMessage(data.message)
        } else{
          p.setData(data.user)
        }
      })
  }

  const submitSignup = async(e) => {
    e.preventDefault();
    if(form.checked){
      await
        register(form).then(data => {
          console.log(data)
          if(data.message) {
            setMessage(data.message)
            console.log(message)
          } else{
            p.setData(data.user)
          }
        })
    } else {
        setMessage("Please accept our privacy policy.")
    }
  }

  return (
    <Fragment>
      <animated.div style={spring} />
        {transitions.map(({ item, key, props }) =>
          item ? (
            <div key={key} className={"z-10 flex absolute w-screen h-screen top-0 left-0"}>

              <animated.div
                className={`popup md:max-w-4xl md:h-2/4 xs:w-full justify-center flex lg:flex-row xs:flex-col fixed rounded left-0 right-0 mx-auto self-center -mt-3 sm:mt-0`}
                style={ props }
                >

                <button className={"absolute right-0 p-4 xs:top-0"} onClick={() => p.onClick(false)}><Close/></button>

                <div className={"hidden bg-gray-900 rounded rounded-r-none w-2/4 lg:flex items-center justify-center "}>
                  <div className="text-gray-100 header__logo--white self-centertext-6xl xs:mx-auto sm:mx-0">
                    <Logo/>
                  </div>
                </div>

                <div className={"popup__forms flex flex-col xs:mx-auto xs:w-full justify-center popup__form rounded rounded-l-none lg:w-2/4 p-5"}>
                  { showLogin ? (
                      <LoginForm submitLogin={submitLogin} updateField={updateField} formMessage={message} formData={form}/>
                    ) : (
                      <SignUpForm submitSignup={submitSignup} updateField={updateField} formMessage={message} formData={form}/>
                    )
                  }

                  { showLogin ? (
                      <div className="my-4 text-center">Not a member? <a onClick={() => toggleLogin(false)} className={"cursor-pointer text-blue-500 hover:text-blue-400 underline"}>Sign Up</a></div>
                    ) : (
                      <div className="my-4 text-center">Already a member? <a onClick={() => toggleLogin(true)} className={"cursor-pointer text-blue-500 hover:text-blue-400 underline"}>Login</a></div>
                    )
                  }
                </div>

              </animated.div>

              <animated.div
                className={`popup-background fixed top-0 left-0 w-full items-center justify-center h-screen`}
                onClick={() => p.toggle(!p.state)}
                style={props}>
              </animated.div>

            </div>

          ) : null
        )
      }
    </Fragment>
  )
}

All of these are updated by the same function:

const updateField = e => {
    setValues({
      ...form,
      [e.target.name]: e.target.type === 'checkbox' ? e.target.checked : e.target.value
    });
  };

  const [form, setValues] = useState({
    username: '',
    password: '',
    email: ''
  });

Live example, click on "sign up" or "login" and try to input values.

I'm pretty lost, what's causing this?

I've looked at this, this and this but they don't seem to have anything to do with my problem.

lpetrucci
  • 1,285
  • 4
  • 22
  • 40
  • I can type all the input fields on my android phone – sam Oct 14 '19 at 15:50
  • I'm using an iPhone so that could be part of the problem? Even then I'm not sure why it would happen exclusively on iPhone. Thank you for testing! – lpetrucci Oct 14 '19 at 16:03
  • Yeah, it doesn't work on iphone, both safari and chrome tested. And it seems doesn't work on safari on Mac either. Could you post more code, the related components? – sam Oct 14 '19 at 16:09
  • Added full component and containing component with all their classes. – lpetrucci Oct 14 '19 at 16:14
  • Some of the code comes from React-Spring, let me know if I should remove that. Classes are defined by TailwindCSS. – lpetrucci Oct 14 '19 at 16:15
  • The form handling code is just working fine, I created a minimum version of the form, it doesn't have any problem on iphone and safari. Maybe a bug or misuse of the animation library causes the problem. – sam Oct 14 '19 at 17:31
  • After removing all react-spring code from it I realised it's actually caused by a CSS rule. I'll write an answer about it. – lpetrucci Oct 14 '19 at 18:38

2 Answers2

5

This happened because one of my inputs had this CSS rule:

.form__username {
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

Once I removed it it started working properly.

lpetrucci
  • 1,285
  • 4
  • 22
  • 40
0

I faced this issue due to the parent component of my input being rerendered endlessly. It was caused by an infinite hook loop. i.e.

const [count, setCount] = useState(0);

// This useEffect loops forever, constantly resetting the value of the input
useEffect(() => {
  ...
  setCount(0);
  ...
}, [count]);

return (
  ...
  <input
    value={count}
    onChange={(e) => setCount(e.target.value)}
  />
  ...
);

Make sure your parent component isn't rerendering endlessly, otherwise your input will rerender endlessly as well, which can cause it to continuously reset its value.

Nick
  • 49
  • 4