Randy
Randy

Reputation: 1287

Why can't I type anything into my form inputs

I have a ReactJS app. I am trying to implement form validation by the accepted answer here as my example. I had to adapt it some because I am using a Function and not a Component (i.e. I don't have a constructor).

Here is the whole function:

import React, { useState } from "react";
import Config from 'config';
import PropTypes from "prop-types";
import "./Login.css";

export default function ChangePassword({ setToken }) {
    const [fields, setFields] = useState({});
    const [errors, setErrors] = useState({});
    
    const tokenString = sessionStorage.getItem("token");
    const token = JSON.parse(tokenString);

    let headers = new Headers({
        "Accept": "application/json",
        "Content-Type": "application/json",
        'Authorization': 'Bearer ' + token.token
    });

    async function changePassword(password) {
        return fetch(Config.apiUrl + "/api/Users/ChangePassword", {
            method: "POST",
            headers: headers,
            body: password
        })
            .then((response) => {
                return response.json();
            });
    }

    function handleValidation() {
        let formErrors = {};
        let formIsValid = true;

        //Password
        if (!fields["password"]) {
            formIsValid = false;
            formErrors["password"] = "Cannot be empty";
        }

        if (typeof fields["password"] !== "undefined") {
            if (!fields["password"].match(/^[a-zA-Z0-9!@#$%^&?]+$/)) {
                formIsValid = false;
                formErrors["password"] = "Only accepted";
            }
        }

        //Confirm Password
        if (!fields["confirmPassword"]) {
            formIsValid = false;
            formErrors["confirmPassword"] = "Cannot be empty";
        }

        if (typeof fields["confirmPassword"] !== "undefined") {
            if (!fields["confirmPassword"].match(/^[a-zA-Z0-9!@#$%^&?]+$/)) {
                formIsValid = false;
                formErrors["confirmPassword"] = "Only accepted";
            }
        }

        if (fields["confirmPassword"] != fields["password"]) {
            formIsValid = false;
            formErrors["confirmPassword"] = "Does not match Password";
        }

        setErrors({ formErrors });
        return formIsValid;
    }

    const handleSubmit = async e => {
        e.preventDefault();
        if (handleValidation()) {
            const myToken = await changePassword(fields["password"]);
            setToken(myToken);
        } else {
            alert("Form has errors.")
        }            
    }

    function handleChange(field, e) {
        let myFields = fields;
        myFields[field] = e.target.value;
        setFields({ myFields });        
    }

    return (
        <div className="login wrapper fadeInDown">
            <div className="login formContent ">
                <h1 className="login centered">Change Password</h1>                
                <form onSubmit={handleSubmit}>                    
                    <div className="centered">
                        <span style={{ color: "red" }}>{errors["password"]}</span>
                        <input type="password" value={fields["password"] || ""} onChange={e => handleChange.bind(this, "password")} id="login" className="login fadeIn second" placeholder="Password" />
                    </div>
                    <div className="centered">
                        <span style={{ color: "red" }}>{errors["confirmPassword"]}</span>
                        <input type="password" value={fields["confirmPassword"] || ""} onChange={e => handleChange.bind(this, "confirmPassword")} id="login" className="login fadeIn third" placeholder="Confirm Password" />
                    </div>
                    <div className="centered">
                        <input type="submit" className="login fadeIn fourth" value="Submit" />
                    </div>
                </form>
            </div>  
        </div>
    )
}

ChangePassword.propTypes = {
    setToken: PropTypes.func.isRequired
}

The function loads, but I cannot type anything in Password or Confirm Password. For the life of me I can't figure out why. I am hoping a kind SO user will point out my error(s).

UPDATE

Just to be complete... Based on the comment by @evolutionxbox I changed my function changePassword to:

async function changePassword(password) {
    const response = await fetch(Config.apiUrl + "/api/Users/ChangePassword", {
        method: "POST",
        headers: headers,
        body: JSON.stringify(password)
    })
    const json = await response.json();
    console.log(json);
    return json;
}

Upvotes: 0

Views: 140

Answers (2)

MrGeet
MrGeet

Reputation: 9

function handleChange(field, e) {
            let myFields = fields;
            myFields[field] = e.target.value;
            setFields({ myFields });        
        }

It is a mistake that most new people in react make. UseState is immutable and when you are saying

 let myFields = fields;

You are copying a reference to your fields useState. Try destructuring, that will copy the value not reference

 let myFields = [...fields];

But my advice would be to create 2 useStates one for password, another for confirmPassword, it will make your code clearer and wont mess up the states. Then change your input element in jsx from that

<input type="password" value={fields["password"] || ""} onChange={e => handleChange.bind(this, "password")} id="login" className="login fadeIn second" placeholder="Password" />

to that

<input type="password" value={fields["password"] || ""} onChange={e => setPassword(e.target.value)} id="login" className="login fadeIn second" placeholder="Password" />

Upvotes: 0

imjared
imjared

Reputation: 20584

As other commenters mentioned, you don't need to .bind in a non-class context.

Change your onChange event handlers from

onChange={e => handleChange.bind(this, "password")}

Add a name attribute to your input elements:

<input name="password" ... />
<input name="confirmPassword" ... />

to

onChange={handleChange}

Then you can update your handleChange function to the following:

function handleChange(e) {
  setFields({
    ...fields,
    [e.target.name]: e.target.value
  });
}    

The above setState has the added benefit of not mutating your existing state object

Upvotes: 3

Related Questions