React Form: fetching data entered by user - reactjs

I'm working on a SignUp Form, I'm trying to fetch the data entered by the user but for some reason I'm getting just null values, like the next example:
So, for the task I'm using two components, Signup and Input, the bellow code is from Signup.js:
import React, { useContext, useState, useRef } from "react";
import Modal from "../UI/Modal";
import classes from "./Login.module.css";
import Input from "../UI/Input/Input";
const Signup = (props) => {
const firstnameInputRef = useRef();
const lastnameInputRef = useRef();
const emailInputRef = useRef();
const passwordInputRef = useRef();
const [isCanceling, setIsCanceling] = useState(false);
const [isSaving, setIsSaving] = useState(false);
const [didSave, setDidSave] = useState(false);
const [isErrorOnSave, setIsErrorOnSave] = useState(false);
//const cartCtx = useContext(CartContext);
const errorOnSignupHandler = () => {
setIsErrorOnSave(true);
};
const signupHandler = async (clientData) => {
setIsSaving(true);
const enteredFirstname = firstnameInputRef.current.value;
const enteredLastname = lastnameInputRef.current.value;
const enteredEmail = emailInputRef.current.value;
const enteredPassword = passwordInputRef.current.value;
const newClientData = {
firstname: enteredFirstname,
lastname: enteredLastname,
email: enteredEmail,
password: enteredPassword,
};
console.log(newClientData);
const response = await fetch("http://localhost:3000/clients", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(newClientData),
});
if (!response.ok) {
errorOnSignupHandler();
} else {
setIsSaving(false);
setIsCanceling(false);
setDidSave(true);
//cartCtx.clearCart();
}
};
const isSavingModalContent = <p>Saving new user...</p>;
/* incluir transaccion para verificar si es exitoso o hubo algun error */
const errorOnSavingModalContent = (
<React.Fragment>
<p>The user account could not be created. Please try again later</p>
<div className={classes.actions}>
<button className={classes.button} onClick={props.onClose}>
Close
</button>
</div>
</React.Fragment>
);
const didSaveModalContent = (
<React.Fragment>
<p>User account created, welcome!</p>
<div className={classes.actions}>
<button className={classes.button} onClick={props.onClose}>
Close
</button>
</div>
</React.Fragment>
);
const SignupButtons = (
<React.Fragment>
<button className={classes["button--alt"]} onClick={signupHandler}>
Sign-Up
</button>
<button className={classes["button--alt"]} onClick={props.onClose}>
Close
</button>
</React.Fragment>
);
const modalActions = (
<div className={classes.actions}>{!isCanceling ? SignupButtons : ""}</div>
);
const SignupModalContent = (
<React.Fragment>
<Input
ref={firstnameInputRef}
id="firstname"
label="First Name"
type="text"
//isValid={emailIsValid}
//value={emailState.value}
//onChange={emailChangeHandler}
//onBlur={validateEmailHandler}
/>
<Input
ref={lastnameInputRef}
id="lastname"
label="Last Name"
type="text"
//isValid={emailIsValid}
//value={emailState.value}
//onChange={emailChangeHandler}
//onBlur={validateEmailHandler}
/>
<Input
ref={emailInputRef}
id="email"
label="E-Mail"
type="email"
autodata="off"
//isValid={emailIsValid}
//value={emailState.value}
//onChange={emailChangeHandler}
//onBlur={validateEmailHandler}
/>
<Input
ref={passwordInputRef}
id="paswword"
label="Password"
type="password"
autodata="new-password"
//isValid={passwordIsValid}
//value={passwordState.value}
//onChange={passwordChangeHandler}
//onBlur={validatePasswordHandler}
/>
<Input
//ref={passwordInputRef}
id="paswword2"
label="Confirm-Password"
type="password"
autodata="new-password"
//isValid={passwordIsValid}
//value={passwordState.value}
//onChange={passwordChangeHandler}
//onBlur={validatePasswordHandler}
/>
{modalActions}
</React.Fragment>
);
return (
<Modal onClose={props.onClose}>
{!isCanceling && !isSaving && !isErrorOnSave && !didSave && SignupModalContent}
{isSaving && isSavingModalContent}
{isErrorOnSave && errorOnSavingModalContent}
{!isSaving && didSave && didSaveModalContent}
</Modal>
);
};
export default Signup;
And this is the code from Input.js:
import React from 'react';
import classes from './Input.module.css';
const Input = React.forwardRef((props, ref) => {
return(
<div className={classes.input}>
<label htmlFor={props.input.id}>{props.label}</label>
<input ref={ref} {...props.input}/>
</div>
);
});
export default Input;
well, basically my question is what am I missing to the get the input data from the user?
Thanks a lot for your comments.

You generally shouldn't use refs the way you're using them.
Instead, have a state for each input, and have the input value equal to the state. When the input changes, update the state.
This way, the state always reflects the state of the form, and is synced up with it. It's a patter called "controlled components". See here for more details.

At a cursory look (without creating a sample app), it seems Input.js has an issue.
I have updated your component below:
import React from 'react';
import classes from './Input.module.css';
const Input = React.forwardRef((props, ref) => {
return(
<div className={classes.input}>
<label htmlFor={props.id}>{props.label}</label>
// It shouldn't be props.input
<input ref={ref} {...props}/>
</div>
);
});
export default Input;
I hope this fixes your issue, otherwise it would be a nice if you could create a sample app in codesandbox or another shareable place.

Related

React Form: Input type password in readonly when trying to fetch State

I'm working on a Sign Up form using React, I'm having troubles with password field, for some reason I can't type nothing in there, it is just like in read only mode, this is the code of my component:
import React, { useState, useRef } from "react";
import Modal from "../UI/Modal";
import classes from "./Login.module.css";
import Input from "../UI/Input/Input";
const Signup = (props) => {
const [firstNameValue, setFirstNameValue] = useState('');
const [lastNameValue, setLastNameValue] = useState('');
const [emailClientValue, setEmailClientValue] = useState('');
const [passwordClientValue, setPasswordClientValue] = useState('12345');
const [isCanceling, setIsCanceling] = useState(false);
const [isSaving, setIsSaving] = useState(false);
const [didSave, setDidSave] = useState(false);
const [isErrorOnSave, setIsErrorOnSave] = useState(false);
//const cartCtx = useContext(CartContext);
const firstNameValueHandler = (event) => {
setFirstNameValue(event.target.value);
}
const lastNameValueHandler = (event) => {
setLastNameValue(event.target.value);
}
const emailClientValueHandler = (event) => {
setEmailClientValue(event.target.value);
}
const passwordClientValueHandler = (event) => {
setPasswordClientValue(event.target.value);
}
const errorOnSignupHandler = () => {
setIsErrorOnSave(true);
};
const signupHandler = async () => {
setIsSaving(true);
const enteredFirstname = firstNameValue;
const enteredLastname = lastNameValue;
const enteredEmail = emailClientValue;
const enteredPassword = passwordClientValue;
const newClientData = {
firstname: enteredFirstname,
lastname: enteredLastname,
email: enteredEmail,
password: enteredPassword,
};
console.log(newClientData);
const response = await fetch("http://localhost:3000/clients", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(newClientData),
});
if (!response.ok) {
errorOnSignupHandler();
} else {
setIsSaving(false);
setIsCanceling(false);
setDidSave(true);
//cartCtx.clearCart();
}
};
const isSavingModalContent = <p>Saving new user...</p>;
/* incluir transaccion para verificar si es exitoso o hubo algun error */
const errorOnSavingModalContent = (
<React.Fragment>
<p>The user account could not be created. Please try again later</p>
<div className={classes.actions}>
<button className={classes.button} onClick={props.onClose}>
Close
</button>
</div>
</React.Fragment>
);
const didSaveModalContent = (
<React.Fragment>
<p>User account created, welcome!</p>
<div className={classes.actions}>
<button className={classes.button} onClick={props.onClose}>
Close
</button>
</div>
</React.Fragment>
);
const SignupButtons = (
<React.Fragment>
<button className={classes["button--alt"]} onClick={signupHandler}>
Sign-Up
</button>
<button className={classes["button--alt"]} onClick={props.onClose}>
Close
</button>
</React.Fragment>
);
const modalActions = (
<div className={classes.actions}>{!isCanceling ? SignupButtons : ""}</div>
);
const SignupModalContent = (
<React.Fragment>
<Input
onChange={firstNameValueHandler}
id="firstname"
label="First Name"
type="text"
value={firstNameValue}
//isValid={emailIsValid}
//value={emailState.value}
//onChange={emailChangeHandler}
//onBlur={validateEmailHandler}
/>
<Input
onChange={lastNameValueHandler}
id="lastname"
label="Last Name"
type="text"
value={lastNameValue}
//isValid={emailIsValid}
//value={emailState.value}
//onChange={emailChangeHandler}
//onBlur={validateEmailHandler}
/>
<Input
onChange={emailClientValueHandler}
id="email"
label="E-Mail"
type="email"
autodata="off"
value={emailClientValue}
//isValid={emailIsValid}
//value={emailState.value}
//onChange={emailChangeHandler}
//onBlur={validateEmailHandler}
/>
<Input
onchange={passwordClientValueHandler}
id="paswword"
label="Password"
type="password"
autodata="new-password"
value={passwordClientValue}
//isValid={passwordIsValid}
//value={passwordState.value}
//onChange={passwordChangeHandler}
//onBlur={validatePasswordHandler}
/>
<Input
//ref={passwordInputRef}
id="paswword2"
label="Confirm-Password"
type="password"
autodata="new-password"
//isValid={passwordIsValid}
//value={passwordState.value}
//onChange={passwordChangeHandler}
//onBlur={validatePasswordHandler}
/>
{modalActions}
</React.Fragment>
);
return (
<Modal onClose={props.onClose}>
{!isCanceling && !isSaving && !isErrorOnSave && !didSave && SignupModalContent}
{isSaving && isSavingModalContent}
{isErrorOnSave && errorOnSavingModalContent}
{!isSaving && didSave && didSaveModalContent}
</Modal>
);
};
export default Signup;
The Input.js component is this:
import React from 'react';
import classes from './Input.module.css';
const Input = React.forwardRef((props, ref) => {
return(
<div className={classes.input}>
<label htmlFor={props.id}>{props.label}</label>
<input ref={ref} {...props}/>
</div>
);
});
export default Input;
I found the problem is in this line:
value={passwordClientValue}
If I remove it I can type in the textbox but I can't fetch the data, also I tried to use a standard html <input type="password> but still can't type in there.
My question: what else do I need to do in order to type in the textbox and get the data?
Thanks a lot.

How to do validation using useRef()

How do I validate input box value using useRef .
Initial validation is not required once user clicks on input box and comes out then it should validate if input box is empty it should show input box cannot be empty.
Codesandbox Link
code i tried. using onBlur
export default function App() {
const name = React.useRef("");
const nameBlurData = (name) => {
console.log("name", name);
};
return (
<div className="App">
<form>
<input
onBlur={() => nameBlurData(name.current.value)}
type="text"
ref={name}
placeholder="Enter First Name"
/>
// show error message here
</form>
</div>
);
}
You can use "useRef" to validate the value of an input field.
No need to use "useState".
Below code is a basic implementation of OP's question
You can replace the "console.log" with your alert component.
import { useRef } from "react";
const ComponentA = () => {
const emailRef = useRef(null);
const passwordRef = useRef(null);
const onBlurHandler = (refInput) => {
if (refInput.current?.value === "") {
console.log(`${refInput.current.name} is empty!`);
}
}
return (
<form>
<input ref={emailRef} onBlur={onBlurHandler.bind(this, emailRef)} />
<input ref={passwordRef} onBlur={onBlurHandler.bind(this, passwordRef)} />
<form/>
)
}
Link to "useRef"
Note: Not tested, code typed directly to SO's RTE
You can use a local state and conditionally render an error message like this:
const [isValid, setIsValid] = useState(true)
const nameBlurData = (name) => {
setIsValid(!!name);
};
return (
<div className="App">
<form>
<input
onBlur={() => nameBlurData(name.current.value)}
type="text"
ref={name}
placeholder="Enter First Name"
/>
{!isValid && <span> input must not be empty </span> }
</form>
Note that you don't really need a ref in this case, you can just use the event object like:
onBlur={(event) => nameBlurData(event.target.value)}
You need to use useState hook to update the value of the name property. Using ref is not ideal here.
Live demo https://stackblitz.com/edit/react-apqj86?devtoolsheight=33&file=src/App.js
import React, { useState } from 'react';
export default function App() {
const [name, setName] = useState('');
const [hasError, setError] = useState(false);
const nameBlurData = () => {
if (name.trim() === '') {
setError(true);
return;
}
setError(false);
};
return (
<div className="App">
<form>
<input
onBlur={nameBlurData}
type="text"
value={name}
onChange={e => setName(e.target.value)}
placeholder="Enter First Name"
/>
{hasError ? <p style={{ color: 'red' }}>Name is required</p> : null}
</form>
</div>
);
}

custom hook to simplify react controlled form

i'd like to simplify form creation avoiding to write for each fied value={} and onChange={} using a custom hook.
this is my code:
https://codesandbox.io/s/busy-noether-pql8x?file=/src/App.js
the problem is that, each time i press the button, the state is cleaned except for the field i've currently edited
import React, { useState } from "react";
import "./styles.css";
const useFormField = (initialValue) => {
const [values, setValues] = React.useState(initialValue);
const onChange = React.useCallback((e) => {
const name = e.target.name;
const value = e.target.value;
setValues( (prevValues) => ({...prevValues,[name]:value}));
}, []);
return { values, onChange };
};
export default function App() {
//hoot to simplify the code,
const {values,onChange} = useFormField({
Salary: "",
Email: ""
})
const onCreateUser = () => {
console.log(values);
};
return (
<div className="App">
<form>
<label htmlFor="Email">Email: </label>
<input name="Email" onChange={onChange} />
<label htmlFor="Email">Salary: </label>
<input name="Salary" onChange={onChange} />
<button type="button" onClick={onCreateUser}>
Send
</button>
</form>
</div>
);
}

How to add field to upload image by Redux Form?

I want to make a submit form by Redux-Form which has a image upload field along with other text fields. I have tried the following approach for image upload and the problem is whenever I try to upload image the form gets re-rendered. How can I do it in a proper way? And another thing is How can I send entire form data (including uploaded image) to Back end? I have used here react,redux-form and material-ui
<Box className={classes.controlTitle}>
Upload Organization Logo
</Box>
<Field
name="logo"
type="file"
component={renderField}
placeholder="Upload your organization logo"
className={classes.field}
/>
I suggest using something like react-uploady. It takes care of the file upload for you and you can use any form/components/ui libraries with it:
import React, { useState, useCallback, useMemo, forwardRef } from "react";
import styled, { css } from "styled-components";
import Uploady, {
useBatchAddListener,
useBatchFinishListener,
useUploadyContext
} from "#rpldy/uploady";
import { asUploadButton } from "#rpldy/upload-button";
const MyUploadField = asUploadButton(
forwardRef(({ onChange, ...props }, ref) => {
const [text, setText] = useState("Select file");
useBatchAddListener((batch) => {
setText(batch.items[0].file.name);
onChange(batch.items[0].file.name);
});
useBatchFinishListener(() => {
setText("Select file");
onChange(null);
});
return (
<div {...props} ref={ref} id="form-upload-button" title={text}>
{text}
</div>
);
})
);
const MyForm = () => {
const [fields, setFields] = useState({});
const [fileName, setFileName] = useState(null);
const uploadyContext = useUploadyContext();
const onSubmit = useCallback(() => {
uploadyContext.processPending({ params: fields });
}, [fields, uploadyContext]);
const onFieldChange = useCallback(
(e) => {
setFields({
...fields,
[e.currentTarget.id]: e.currentTarget.value
});
},
[fields, setFields]
);
const buttonExtraProps = useMemo(
() => ({
onChange: setFileName
}),
[setFileName]
);
return (
<Form>
<MyUploadField autoUpload={false} extraProps={buttonExtraProps} />
<br />
<input
onChange={onFieldChange}
id="field-name"
type="text"
placeholder="your name"
/>
<br />
<input
onChange={onFieldChange}
id="field-age"
type="number"
placeholder="your age"
/>
<br />
<button>
id="form-submit"
type="button"
onClick={onSubmit}
disabled={!fileName}
>
Submit Form
</button>
</Form>
);
};
export default function App() {
return (
<div className="App">
<Uploady
clearPendingOnAdd
destination={{ url: "[upload-url]" }}
multiple={false}
>
<MyForm />
</Uploady>
</div>
);
}
You can check out this sandbox for a complete example.

Trying to bind submit and save to localStorage

Here my App.js code, I am trying to bind and capture the "handlesubmit" function, and then append to an empty list which will be populated. Thanks.
import React from 'react';
const App = () => {
const [songs, setSongs] = React.useState([]);
React.useEffect(() => {
const data = localStorage.getItem('songs');
if (!data) { }
setSongs(JSON.parse(data));
}, []);
React.useEffect(() => {
localStorage.setItem('songs', JSON.stringify(songs));
});
const handleSubmit = data => {
setSongs([data]);
}
return (
<main>
<h1>Music Editor</h1>
<form onSubmit={this.props.handleSubmit(this.handleSubmit.bind(this))} autoComplete="false">
<label for="title">Title:</label>
<input type="text" id="title" name="title" placeholder="Type title/name of song" value="" />
<input type="submit" value="Add song" />
</form>
</main>
);
}
export default App;
The explanation is commented in the code itself.
Here is the codesandbox link to see the App working.
import React from 'react';
const App = () => {
const [songs, setSongs] = React.useState([]);
// use another state for song title
const [songTitle, setSongTitle] = React.useState('');
React.useEffect(() => {
const data = localStorage.getItem('songs');
// only update the state when the data persists
if (data) setSongs(JSON.parse(data));
}, []);
// update the localStorage whenever the songs array changes
React.useEffect(() => {
localStorage.setItem('songs', JSON.stringify(songs));
}, [songs]);
// inside the functional component, there is no "this" keyword
const handleSubmit = (event) => {
event.preventDefault();
// append the new song title with the old one
setSongs([
...songs,
songTitle
]);
}
return (
<main>
<h1>Music Editor</h1>
<form onSubmit={handleSubmit} autoComplete="false">
<label htmlFor="title">Title:</label>
<input
type="text"
id="title"
name="title"
placeholder="Type title/name of song"
value={songTitle}
onChange={e => setSongTitle(e.target.value)}
/>
<input type="submit" value="Add song" />
</form>
</main>
);
}
export default App;

Resources