Jc John
Jc John

Reputation: 1859

Binding input type using refs in reactjs

I am a beginner in reactJS and would like to ask a simple question. I am following some tutorials and I really don't know why this wont work on mine. I know my question is very simple since I am a beginner. Please have a patience with my question.

I have this code from a function component form:

function Login() {

  const emailInputRef = useRef();
  const passwordInputRef = useRef();

  function submitHandler(e) {          
    e.preventDefault();
    const emailInput = emailInputRef.current.value;
    const passwordInput = passwordInputRef.current.value;
      
    console.log(emailInput);
  }

  return (
    <>
      <Col lg="5" md="7">
        <Card className="bg-secondary shadow border-0">                     
          <CardBody className="px-lg-5 py-lg-5">
            <div className="text-center text-muted mb-4">
              <small>Sign in</small>
            </div>
            <Form role="form" onSubmit={submitHandler}>
              <FormGroup className="mb-3">
                <InputGroup className="input-group-alternative">
                  <InputGroupAddon addonType="prepend">
                    <InputGroupText>
                      <i className="ni ni-email-83" />
                    </InputGroupText>
                  </InputGroupAddon>
                  <Input
                    placeholder="Email"
                    type="email"
                    autoComplete="new-email"
                    ref={emailInputRef}
                  />
                </InputGroup>
              </FormGroup>
              <FormGroup>
                <InputGroup className="input-group-alternative">
                  <InputGroupAddon addonType="prepend">
                    <InputGroupText>
                      <i className="ni ni-lock-circle-open" />
                    </InputGroupText>
                  </InputGroupAddon>
                  <Input
                    placeholder="Password"
                    type="password"
                    autoComplete="new-password"
                    ref={passwordInputRef}
                  />
                </InputGroup>
              </FormGroup>
              <div className="custom-control custom-control-alternative custom-checkbox">
                <input
                  className="custom-control-input"
                  id=" customCheckLogin"
                  type="checkbox"
                />
                <label
                  className="custom-control-label"
                  htmlFor=" customCheckLogin"
                >
                  <span className="text-muted">Remember me</span>
                </label>
              </div>
              <div className="text-center">
                <Button className="my-4" color="primary" type="submit">
                  Sign in
                </Button>
              </div>
            </Form>
          </CardBody>
        </Card>
        <Row className="mt-3">
          <Col xs="6">
            <a
              className="text-light"
              href="#pablo"
              onClick={(e) => e.preventDefault()}
            >
              <small>Forgot password?</small>
            </a>
          </Col>
          <Col className="text-right" xs="6">
            <a
              className="text-light"
              href="#pablo"
              onClick={(e) => e.preventDefault()}
            >
              <small>Create new account</small>
            </a>
          </Col>
        </Row>
      </Col>
    </>
  );
}

I want to bind my input type from my submitHandler and log the values of the input types. I followed a tutorial using refs and I don't know if I made something wrong about it since I was a beginner. When I logged the values it gives me undefined values.

Upvotes: 3

Views: 1788

Answers (3)

Reinier Garcia
Reinier Garcia

Reputation: 1690

You have a few options for that. You can use the useRef React hook or the state stored using the useState React Hook. In both cases you need to import the related hook first. You could use both if you want. You could also use a custom hook with the useReducer React hook.

In your case it would be something like these:

Option # 1: Using useRef

import {useRef} from "react";

function Login() {
    const emailInputRef = useRef();
    const passwordInputRef = useRef();

    function submitFormHandler(event) {
        event.preventDefault();
        const email = emailInputRef.current.value;
        const password = passwordInputRef.current.value;

        console.log(`email = ${email} & password = ${password}`);
    }

    return (
        <>
            <Col lg="5" md="7">
                <Card className="bg-secondary shadow border-0">
                    <CardBody className="px-lg-5 py-lg-5">
                        <div className="text-center text-muted mb-4">
                            <small>Sign in</small>
                        </div>
                        <Form role="form" onSubmit={submitFormHandler}>
                            <FormGroup className="mb-3">
                                <InputGroup className="input-group-alternative">
                                    <InputGroupAddon addonType="prepend">
                                        <InputGroupText>
                                            <i className="ni ni-email-83"/>
                                        </InputGroupText>
                                    </InputGroupAddon>
                                    <Input
                                        placeholder="Email"
                                        type="email"
                                        id='email'
                                        name='email'
                                        required
                                        autoComplete="new-email"
                                        ref={emailInputRef}
                                    />
                                </InputGroup>
                            </FormGroup>

                            <FormGroup>
                                <InputGroup className="input-group-alternative">
                                    <InputGroupAddon addonType="prepend">
                                        <InputGroupText>
                                            <i className="ni ni-lock-circle-open"/>
                                        </InputGroupText>
                                    </InputGroupAddon>
                                    <input
                                        placeholder="Password"
                                        type="password"
                                        id="password"
                                        name="password"
                                        required
                                        autoComplete="new-password"
                                        ref={passwordInputRef}
                                    />
                                </InputGroup>
                            </FormGroup>

                            <div className="custom-control custom-control-alternative custom-checkbox">
                                <input
                                    className="custom-control-input"
                                    id=" customCheckLogin"
                                    type="checkbox"
                                />
                                <label
                                    className="custom-control-label"
                                    htmlFor=" customCheckLogin"
                                >
                                    <span className="text-muted">Remember me</span>
                                </label>
                            </div>
                            <div className="text-center">
                                <Button className="my-4" color="primary" type="submit">
                                    Sign in
                                </Button>
                            </div>
                        </Form>
                    </CardBody>
                </Card>
                <Row className="mt-3">
                    <Col xs="6">
                        <a
                            className="text-light"
                            href="#pablo"
                            onClick={(e) => e.preventDefault()}
                        >
                            <small>Forgot password?</small>
                        </a>
                    </Col>
                    <Col className="text-right" xs="6">
                        <a
                            className="text-light"
                            href="#pablo"
                            onClick={(e) => e.preventDefault()}
                        >
                            <small>Create new account</small>
                        </a>
                    </Col>
                </Row>
            </Col>
        </>
    );
    };

export default Login;

Option # 2: Using useState

import {useState} from "react";

function Login() {
    const [emailState, setEmailState] = useState('');
    const [passwordState, setPasswordState] = useState('');

    function submitFormHandler(event) {
        event.preventDefault();

        console.log(`email = ${emailState} & password = ${passwordState}`);
    }

    const onEmailChangeHandler = (event) => {
        setEmailState(event.current.value);

    };
    const onPasswordChangeHandler = (event) => {
        setPasswordState(event.current.value);
    };

    return (
        <>
            <Col lg="5" md="7">
                <Card className="bg-secondary shadow border-0">
                    <CardBody className="px-lg-5 py-lg-5">
                        <div className="text-center text-muted mb-4">
                            <small>Sign in</small>
                        </div>
                        <Form role="form" onSubmit={submitFormHandler}>
                            <FormGroup className="mb-3">
                                <InputGroup className="input-group-alternative">
                                    <InputGroupAddon addonType="prepend">
                                        <InputGroupText>
                                            <i className="ni ni-email-83"/>
                                        </InputGroupText>
                                    </InputGroupAddon>
                                    <input
                                        placeholder="Email"
                                        type="email"
                                        id='email'
                                        name='email'
                                        required
                                        autoComplete="new-email"
                                        value={emailState}
                                        onChange={onEmailChangeHandler}
                                    />
                                </InputGroup>
                            </FormGroup>

                            <FormGroup>
                                <InputGroup className="input-group-alternative">
                                    <InputGroupAddon addonType="prepend">
                                        <InputGroupText>
                                            <i className="ni ni-lock-circle-open"/>
                                        </InputGroupText>
                                    </InputGroupAddon>
                                    <input
                                        placeholder="Password"
                                        type="password"
                                        id="password"
                                        name="password"
                                        required
                                        autoComplete="new-password"
                                        value={passwordState}
                                        onChange={onPasswordChangeHandler}
                                    />
                                </InputGroup>
                            </FormGroup>

                            <div className="custom-control custom-control-alternative custom-checkbox">
                                <input
                                    className="custom-control-input"
                                    id=" customCheckLogin"
                                    type="checkbox"
                                />
                                <label
                                    className="custom-control-label"
                                    htmlFor=" customCheckLogin"
                                >
                                    <span className="text-muted">Remember me</span>
                                </label>
                            </div>
                            <div className="text-center">
                                <Button className="my-4" color="primary" type="submit">
                                    Sign in
                                </Button>
                            </div>
                        </Form>
                    </CardBody>
                </Card>
                <Row className="mt-3">
                    <Col xs="6">
                        <a
                            className="text-light"
                            href="#pablo"
                            onClick={(e) => e.preventDefault()}
                        >
                            <small>Forgot password?</small>
                        </a>
                    </Col>
                    <Col className="text-right" xs="6">
                        <a
                            className="text-light"
                            href="#pablo"
                            onClick={(e) => e.preventDefault()}
                        >
                            <small>Create new account</small>
                        </a>
                    </Col>
                </Row>
            </Col>
        </>
    );
};

export default Login;

Option # 3: Using both React hooks (useRef & useState).

You could use the useRef React hook for focusing purposes only and also use a local state value, using the useState React hook, for the usual purposes of login in.

Option # 4: Using custom hooks.

You can use all the mentioned before, but using primordially a custom hook, which uses the useReducer React hook under the trunk, and then useRef for focusing and useState for other purposes:

Coder example:

Custom hook:

import {useReducer} from "react";

const initialInputState = {
    valueState: '',
    valueIsTouchedState: false,
};

const inputStateReducer = (state, action) => {
    if (action.type === 'SET_VALUE_IS_TOUCHED_STATE') {
        return {
            valueState: state.valueState,
            valueIsTouchedState: action.payload.valueIsTouchedState
        };
    }

    if (action.type === 'SET_VALUE_STATE') {
        return {
            valueState: action.payload.valueState,
            valueIsTouchedState: state.valueIsTouchedState
        };
    }

    return initialInputState;
};

const useInputReducer = (valueValidator) => {
    const [inputState, dispatchFunction] = useReducer(inputStateReducer, initialInputState);

    const valueIsValidState = valueValidator(inputState.valueState);
    const valueInputIsInvalid = (!valueIsValidState && inputState.valueIsTouchedState);

    const valueInputChangeHandler = (event) => {
        dispatchFunction({type: 'SET_VALUE_IS_TOUCHED_STATE', payload: {valueIsTouchedState: true}});
        dispatchFunction({type: 'SET_VALUE_STATE', payload: {valueState: event.target.value}});
    };

    const valueInputBlurHandler = (event) => {
        dispatchFunction({type: 'SET_VALUE_IS_TOUCHED_STATE', payload: {valueIsTouchedState: true}});
        // setValueState(event.target.value);
    };

    const setValueIsTouchedState = (value) => {
        dispatchFunction({type: 'SET_VALUE_IS_TOUCHED_STATE', payload: {valueIsTouchedState: value}});
    };

    const resetValueInput = () => {
        dispatchFunction({type: 'SET_VALUE_STATE', payload: {valueState: ''}});
        dispatchFunction({type: 'SET_VALUE_IS_TOUCHED_STATE', payload: {valueIsTouchedState: false}});
    };

    return {
        valueState: inputState.valueState,
        setValueIsTouchedState,
        valueIsValidState,
        valueInputIsInvalid,
        valueInputChangeHandler,
        valueInputBlurHandler,
        resetValueInput,
    };
};

export default useInputReducer;

Form:

import {Fragment, useRef, useState} from 'react';
import {Prompt} from "react-router-dom";

import useInputReducer from "../../hooks/use-input-reducer";
import * as validators from "../../tools/validators";

import styles from './AuthForm.module.css';

const AuthForm = () => {
    const [isLogin, setIsLogin] = useState(true);
    const [startedToWork, setStartedToWork] = useState(false);

    const {
        valueState: emailState,
        setValueIsTouchedState: setEmailIsTouchedState,
        valueIsValidState: emailIsValidState, valueInputIsInvalid: emailInputIsInvalid,
        valueInputChangeHandler: emailInputChangeHandler,
        // valueInputBlurHandler: emailInputBlurHandler,
        resetValueInput: resetEmailInput,
    } = useInputReducer(validators.emailValidator);

    const {
        valueState: passwordState,
        setValueIsTouchedState: setPasswordIsTouchedState,
        valueIsValidState: passwordIsValidState, valueInputIsInvalid: passwordInputIsInvalid,
        valueInputChangeHandler: passwordInputChangeHandler,
        // valueInputBlurHandler: passwordInputBlurHandler,
        resetValueInput: resetPasswordInput,
    } = useInputReducer(validators.nameValidator);

    const formIsValid = (emailIsValidState && passwordIsValidState);
    const emailInputRef = useRef();
    const passwordInputRef = useRef();


    const submitFormHandler = (event) => {
        event.preventDefault();
        setEmailIsTouchedState(true);
        setPasswordIsTouchedState(true);
        emailInputRef.current.focus();

        if (!formIsValid) {
            setProperFocus();
            return;
        }

        console.log('Submitted!. I did something!!');

        const email = emailInputRef.current.value;
        const password = passwordInputRef.current.value;

        resetEmailInput();
        resetPasswordInput();
    };

    const setProperFocus = () => {
        if (emailInputIsInvalid) {
            emailInputRef.current.focus();
        } else if (passwordInputIsInvalid) {
            passwordInputRef.current.focus();
        }
    }

    const switchAuthModeHandler = () => {
        setIsLogin((prevState) => !prevState);
    };

    const onStartedToWorkHandler = () => {
        setStartedToWork(true);
    };

    const onFinishEnteringHandler = () => {
        setStartedToWork(false);
    };

    const emailValidityClasses = `${styles.control}${emailInputIsInvalid ? ` ${styles.invalid}` : ''}`;
    const passwordValidityClasses = `${styles.control}${passwordInputIsInvalid ? ` ${styles.invalid}` : ''}`;

    return (
        <Fragment>
            <Prompt
                when={startedToWork}
                message={location =>
                    `Are you sure you want to go to "${location.pathname}"? \n All your entered data will be lost.`
                }
            />

            <section className={styles.auth}>
                <h1>{isLogin ? 'Login' : 'Sign Up'}</h1>
                <form
                    onSubmit={submitFormHandler}
                    onChange={onStartedToWorkHandler}
                >

                    <div className={emailValidityClasses}>
                        <label htmlFor='email'>Your Email</label>
                        <input
                            type='email'
                            id='email'
                            name='email'
                            required
                            autoFocus={true}
                            ref={emailInputRef}
                            value={emailState}
                            onChange={emailInputChangeHandler}
                            // onBlur={emailInputBlurHandler}
                        />
                        {emailInputIsInvalid ? <p className={styles['error-text']}>The Email must be valid.</p> : <p>&nbsp;</p>}
                    </div>

                    <div className={passwordValidityClasses}>
                        <label htmlFor='password'>Your Password</label>
                        <input
                            type='password'
                            id='password'
                            required
                            autoComplete="on"
                            ref={passwordInputRef}
                            value={passwordState}
                            onChange={passwordInputChangeHandler}
                            // onBlur={passwordInputBlurHandler}
                        />
                        {passwordInputIsInvalid ? <p className={styles['error-text']}>The Password must not be empty.</p> : <p>&nbsp;</p>}
                    </div>

                    <div className={styles.actions}>
                        <button onClick={onFinishEnteringHandler}>{isLogin ? 'Login' : 'Create Account'}</button>

                        <button
                            type='button'
                            className={styles.toggle}
                            onClick={switchAuthModeHandler}
                        >
                            {isLogin ? 'Create new account' : 'Login with existing account'}
                        </button>
                    </div>
                </form>
            </section>
        </Fragment>
    );
};

export default AuthForm;

Upvotes: 1

lissettdm
lissettdm

Reputation: 13078

To get the current input reference use innerRef property instead of ref:

<Input
   placeholder="Email"
   type="email"
   autoComplete="new-email"
   innerRef={emailInputRef}
/>

const emailInput = emailInputRef.current.value;

ref will only get you a reference to the Input component, innerRef will get you a reference to the DOM input.

Upvotes: 1

ucup
ucup

Reputation: 685

I never do react js but i code react native. I think you can store the input in the state using onChangeText like this

using hooks

    const index = ()=>{
    const {state,setState} = useState('');
    return <TextInput onChangeText={(txt)=>{
    setState(txt);
    console.log(state); }} />
    }

Upvotes: 0

Related Questions