Ok so here's the deal I'm working in react and I got this website me and team are working on for a senior project.
So i've got this functioning login page with its code below called component.jsx its got all i need including text inputs for customers. however We want to add validation to it so that when the text is empty(like if a customer just forget and hit submit) it would send a message letting them know.
essentially Im asking for a conditional only issue is that jsx dosent rly do conditionals like IF/else
so I wanted to do it in a sepereate js file (validate.js) and I've looked everywhere I cant find anything about how to make an effective function/class or whatever for an input in a JS file then impart that into the jsx file where the code is.
So right now Im looking fr guidance on how to achieve that goal and was wondering if anybody had any ideas(websites with this, similar questions or just know the answer).
If you do just know the answer on how to do this please keep it simple that way I can look back at it latter to understand incase I miss you
currently the JS file(validate.js) I have is empty
login website
JSX code
Here is a little exemple, there is plenty of way to manage errors, here is one.
const Login = () => {
// our values state
const [values, setValues] = React.useState({email: "", password: ""});
// our errors state
const [errors, setErrors] = React.useState({email: "", password: ""});
const handleChange = (event) => {
// onChange return an event object containing the name of the input and its current value
const { name, value } = event.target;
// update values in state
setValues({...values, [name]: value})
// reset error for that input on change
if(errors[name].length > 0) setErrors({ ...errors, [name]: "" })
}
const handleSubmit = (event) => {
// on submit we prevent default behavior
event.preventDefault();
// check if our inputs are valid
const {hasErrors, errors} = validateInputs();
if(!hasErrors) {
// input are not empty
// you can do whatever you want here
} else {
// there is errors, update our state to display them on next render
setErrors(errors);
}
}
const isEmpty = (value) => {
return value === "";
}
const validateInputs = () => {
// iterate each input value
const errors = Object.keys(values).reduce((errors, current) => {
// you can add any check here
if(isEmpty(values[current])) {
errors[current] = `${current.charAt(0).toUpperCase() + current.slice(1)} is required.`;
}
return errors;
}, {});
const hasErrors = Object.keys(errors).length > 0;
return { errors, hasErrors };
}
return (
<form onSubmit={handleSubmit}>
<label>Email</label>
<input type="text" placeholder="Email" name="email" onChange={handleChange} />
{ errors.email.length > 0 && ( <div className="error-message" >{errors.email}</div> ) }
<label>Password</label>
<input type="password" placeholder="Password" name="password" onChange={handleChange} />
{ errors.password.length > 0 && ( <div className="error-message" >{errors.password}</div> ) }
<button type="submit" >Log in</button>
</form>
)
};
ReactDOM.render(
<Login />,
document.getElementById("root")
);
form {
display: flex;
flex-direction:column;
}
input, button {
width: 200px;
margin: 0.3rem;
}
.error-message {
color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
The simple and easiest way is to add required:{true} into your attribute.
example here:
<Form.Control
type="text"
placeholder="Enter Skills"
value={Skill}
required={true}
onChange={(e) => setSkill(e.target.value)}
></Form.Control>
Related
As you can see I am trying to apply validation to registration form that is, when user enters values in input fields he should be able to see the validations. Can anyone tell me where i am going wrong here. I am using functional components and the form should validate before submitting.
import React, {useState, useRef, useEffect} from 'react';
const Home = () => {
const [ userRegistration, setUserRegistration ] = useState ({
fullname:"", email:"", phone:"", password:""
});
// States for checking the errors
const [submitted, setSubmitted] = useState(false);
const [record, setRecord] = useState ([]);
const handleInput = (e) => {
setUserRegistration ({...userRegistration, [e.target.name] : e.target.value});
setSubmitted(false);
}
Here I am struggling
// Handling the form submission
const handleSubmit = (e) => {
e.preventDefault();
// const newRecord = {...userRegistration, id: new Date().getTime().toString()};
// setRecord = {...record, newRecord};
// setUserRegistration ({fullname:"", email: "", phone:"", password:""});
{
if (fullname === '' || email === '' || password === '') {
setError(true);
} else {
setSubmitted(true);
setError(false);
}
};
}
// Showing success message
const successMessage = () => {
return (
<div
className="success"
style={{
display: submitted ? '' : 'none',
}}>
<h1>User {fullname} successfully registered!!</h1>
</div>
);
};
// Showing error message if error is true
const errorMessage = () => {
return (
<div
className="error"
style={{
display: error ? '' : 'none',
}}>
<h1>Please enter all the fields</h1>
</div>
);
};
// const handleSubmit = (e) => {
// e.preventDefault();
// const newRecord = {...userRegistration, id: new Date().getTime().toString()};
// setRecord = {...record, newRecord};
// setUserRegistration ({fullname:"", email: "", phone:"", password:""});
// }
return (
<>
{/* Calling to the methods */}
<div className="messages">
{errorMessage()}
{successMessage()}
</div>
<form>
<div>
<label htmlFor="fullname">Fullname</label>
<input type="text" autocomplete="off" onChange={handleInput} value={userRegistration.fullname} name="fullname" id="fullname" />
</div>
<div>
<label htmlFor="email">email</label>
<input type="text" autocomplete="off" onChange={handleInput} value={userRegistration.email} name="fullname" id="fullname" />
</div>
<div>
<label htmlFor="phone">phone</label>
<input type="text" autocomplete="off" onChange={handleInput} value={userRegistration.phone} name="fullname" id="fullname" />
</div>
<div>
<label htmlFor="password">password</label>
<input type="text" autocomplete="off" onChange={handleInput} value={userRegistration.password} name="fullname" id="fullname" />
</div>
<button onClick={handleSubmit}>SUBMIT</button>
</form>
<div>
{
record.map ((curElem) => {
const {id, fullname, email, phone, password} =curElem
return(
<div key={id} >
<p>{fullname}</p>
<p>{email}</p>
<p>{phone}</p>
<p>{password}</p>
</div>
)
}
)
}
</div>
</>
)}
export default Home;
The error is:
src\component\Home.js
Line 31:9: 'fullname' is not defined no-undef
Line 31:28: 'email' is not defined no-undef
Line 31:44: 'password' is not defined no-undef
Line 32:7: 'setError' is not defined no-undef
Line 35:7: 'setError' is not defined no-undef
Line 47:19: 'fullname' is not defined no-undef
Line 58:20: 'error' is not defined no-undef
You havent added state variable for error. (error variable is used in errorMessage function definition)
in successMessage function definition you have to give {userRegistration.fullname} instead of {fullname}
In handleSubmit you are using fullname, email, password but they are not destructured from userRegistration. So you have to give either userRegistration.fullname, etc. like mentioned in 2. or destructure the properties from the userRegistration object before the if condition like
const {fullname,email,password} = userRegistration;
All the inputs are having same 'name' attribute, change to the respective key for it to work correctly.
There are quite a few adjustments that you need to make.
You do not have Error handling useState. You setError state, but you do not have it initiated at the top of the Home component.
From the beginning, you set setSubmitted to false, so unless you change that state to 'true', you do not need to set it to false since it is already false.
Your Submit function receives data on onSubmit event that you should JSON.stringify in order to use it further, but you just change states in your function. Or you can push that form data into the state too.
Your validation is super duper basic just checking if it is an empty string. I would suggest writing up more validations than that.
In successMessage function definition you have to give {userRegistration.fullname} instead of {fullname} .
Your input names are literally the same for all inputs. They must be different, just like "id"s. Your ids are also the same. Your form object keys will be named after "name" of each input field. Change that.
Lastly, use either Formik or Yup or React Hook Form that will assist you tremendously with this process and make it smoother and simplier. For example, read documentation on React Hook Form here(it is quite easy documentation)
https://react-hook-form.com/
I'm extremely new to ReactJS and have been struggling quite a bit. Right now, I'm trying to create a setting in an admin page where the admin can enter text and update a section on the web app's home page. I created const [aboutBox, setAboutBox] = useState("") in Home.js and passed it as props to another file. However, every time I try to type anything in the text box on the admin page, it throws the error "TypeError: props.setAboutBox is not a function". I have used the same format to create other functions and those have worked, so I'm not exactly sure what's going wrong. Any help would be greatly appreciated!! Thank you guys so much!
const HomeAboutForm = (props) => {
const handleInputChange = event => {
console.log('handle input change')
const box = event.target;
props.setAboutBox({ ...props.aboutBox,box})
}
return (
<form>
<TextField
onSubmit = { event => {
event.preventDefault();
if (!props.aboutBox) return
const box = event.target;
props.setAboutBox({ ...props.aboutBox,box})
console.log(props.aboutBox)
}}
placeholder="Enter new text for About Section here"
multiline
value={props.aboutBox}
variant="outlined"
rows={4}
style={{ margin: "10px", width: '600px' }}
rowsMax={6}
required
input type="text" name="name" value={props.aboutBox} onChange={handleInputChange}
>
<input type="text" name="name" value={props.aboutBox} onChange={handleInputChange} />
</TextField>
<p></p>
<input type="submit" value="Submit" />
</form>
);
};
EDIT: The parent component to HomeAboutForm also has its own parent component, but the code calling HomeAboutForm is
<HomeAboutForm
aboutBox = {props.aboutBox}
setAboutBox = {props.setAboutBox}/>
There is however, a function called getChoiceView that is called like {getChoiceView(pageNum,props)} and then from there, the function has the code
const getChoiceView = (pageNum, props) => {
switch (pageNum) {
case 0:
return <AddPhoto />;
case 1:
return <HomeUpdateAboutView
aboutBox = {props.aboutBox}
setAboutBox = {props.setAboutBox}/>;
case 2:
return <HomeUpdateStylistForm />;
default:
return "Unknown pageView";
}
};
Could how I'm calling getChoiceView be the issue?
I have a custom file upload field that uploads files immediately once you select/drop them, and returns a UUID for later submission. So, basically what most webapps do nowadays (e.g. Facebook, Twitter, etc.) when you drop a file.
This is all easy enough to handle with final-form - my field simply calls final-form's onChange function once the upload finished to pass the UUID to final-form.
However, if a user submits the form while an upload is still running they will submit the form without a file UUID since as far as final-form is concerned, no file has been selected yet. Especially for bigger files this would be problematic as users might not realize they still have to wait (even with a loading indicator). Marking the field as required is not an option either, since not providing a file at all is valid (or the field may allow multiple files, or you are replacing a previously-uploaded file) - so the only case where the field is "invalid" is when a file is currently being uploaded.
Here is a codesandbox with a small dummy app that should provide a good starting point at any attempt to solve it: https://codesandbox.io/s/polished-fast-k80t7
The idea is that the field becomes invalid when clicking "Pretend to start uploading" and valid again after clicking "Pretend to finish uploading".
Please note that I'm looking for a clean way to do this while keeping things separated, i.e. I'd prefer to not add state for this to the component containing the Form - also because a validation functions need to be idempotent, so checking external state there would be pretty much broken (as my attempt of doing this shows).
Should the codesandbox links ever break, here's the relevant code from the first link (since the other one is just a broken attempt anyway):
import React, { useState } from "react";
import { render } from "react-dom";
import Styles from "./Styles";
import { Form, Field } from "react-final-form";
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
const onSubmit = async values => {
await sleep(300);
window.alert(JSON.stringify(values, 0, 2));
};
const MyFileUploader = ({ input: { value, onChange }, meta: { invalid } }) => {
const [isUploading, setUploading] = useState(false);
const handleStartClick = () => {
setUploading(true);
};
const handleFinishClick = () => {
setUploading(false);
onChange("0xdeadbeef"); // let's pretend this is the file UUID ;)
};
const style = { color: invalid ? "#f00" : "#000" };
if (value) {
return <em style={style}>{value}</em>;
} else if (isUploading) {
return (
<button type="button" onClick={handleFinishClick} style={style}>
Pretend to finish uploading
</button>
);
} else {
return (
<button type="button" onClick={handleStartClick} style={style}>
Pretend to start uploading
</button>
);
}
};
const App = () => (
<Styles>
<h1>React Final Form</h1>
<Form
onSubmit={onSubmit}
initialValues={{ file: null }}
render={({ handleSubmit, form, submitting, values }) => (
<form onSubmit={handleSubmit}>
<div>
<label>File</label>
<Field name="file" component={MyFileUploader} />
</div>
<div className="buttons">
<button type="submit" disabled={submitting}>
Submit
</button>
<button type="button" onClick={form.reset} disabled={submitting}>
Reset
</button>
</div>
<pre>{JSON.stringify(values, 0, 2)}</pre>
</form>
)}
/>
</Styles>
);
render(<App />, document.getElementById("root"));
Interesting question.
How about something like this? When the file is uploading, it renders a field that will always be invalid, thus blocking the submission.
const SubmitBlocker = ({ children }) => (
<Field name="uploading" validate={() => children}>
{({ meta }) =>
meta.touched && meta.error ? meta.error : null
}
</Field>
);
I have this validation schema for a form made using withFormik() used in my React application, Here validateJql() is my custom validation function for yup
validationSchema: Yup.object().shape({
rework: Yup.string().required("Rework query is required").validateJql(),
originalEstimate: Yup.string().required("Original Estimate query is required").validateJql()
})
and my form Component is like this:
const addSomeForm = (props) => {
const {
values,
touched,
errors,
isSubmitting,
handleChange,
handleSubmit,
} = props;
return (
<form onSubmit={handleSubmit}>
<div className="form-group">
<div>
<label htmlFor="name" className="col-form-label"><b>Rework Query:</b></label>
<textarea id="query.rework" rows="5" type="text" className="form-control" placeholder="Enter JQL with aggregate Function" value={values.query.rework} onChange={handleChange} required />
{errors.query && errors.query.rework && touched.query && <span className="alert label"> <strong>{errors.query.rework}</strong></span>}
</div>
</div>
<div className="form-group">
<div>
<label htmlFor="name" className="col-form-label"><b>Original Estimate:</b></label>
<textarea id="query.originalEstimate" rows="5" type="text" className="form-control" placeholder="Enter JQL with aggregate Function" value={values.query.originalEstimate} onChange={handleChange} required />
{errors.query && errors.query.originalEstimate && touched.query && <span className="alert label"> <strong>{errors.query.originalEstimate}</strong></span>}
</div>
</div>
</form>
)
Now, what I want to do is not to run validation on form submit if the field rework and originalEstimate is not touched and also not empty. How can I achieve this with withFormik HOC or Yup? I have partially been through Yup docs and Formik docs but could not find something to fit with my problem.
This is the case after submitting the form once and editing after that for minor tweaks in some of those multiple fields. if there are multiple fields and only some are edited, I don't want to run validation for all the fields existed.
Thank you in advance.
This is the default desired behavior as stated in formik docs but i think you can do the following:
Instead of using validationSchema, use validate function.
Validate function will work the same way your validationSchema works. You just need to use Yup programmatically from a function with mixed.validate
So you can have the full control of all the props in your form. You could also use the getFieldMeta to get the touched and value of the field and use that in your validation. Or get those props from touched object in form with getIn
Something like:
// Some util functions
function mapYupErrorsToFormikErrors(err: { inner: any[] }) {
return err.inner
.filter((i: { path: any }) => !!i.path)
.reduce(
(curr: any, next: { path: any; errors: any[] }) => ({
...curr,
[next.path]: next.errors[0],
}),
{},
)
}
function validateSchema(values: object, schema: Schema<object>) {
return schema
.validate(values, {
abortEarly: false,
strict: false,
})
.then(() => {
return {}
})
.catch(mapYupErrorsToFormikErrors)
}
// Your validation function, as you are using `withFormik` you will have the props present
function validateFoo(values, props) {
const { touched, value } = props.getFieldMeta('fooFieldName') // (or props.form.getFieldmeta, not sure)
const errors = validateSchema(values, yourYupSchema)
if (!touched && !value && errors.fooFieldName) {
delete errors.fooFieldName
}
return errors
}
Well, touched might not work for your use case because formik probably would set it to true on submission, but there you have all the props and you can use something different, like the empty value or some other state prop you manually set. You got all the control there.
I had a similar issue, I ended up creating another field where I set the value when showing the edit screen. Then i compare inside a test function like this :
originalField: yup.string().default(''),
field: yup.string().default('').required('Field is required.').test('is-test',
'This is my test.',
async (value, $field) => {
if($field.parent.originalField !== '' && value === $field.parent.originalField) return true
return await complexAsyncValidation(value)
}
Not perfect, but definitely working
I am trying to use redux-form to generate a quiz form. My data source for an individual redux-form field component comes from an array - questions in my case. Everything works as expected except validation. Any thoughts how this can be fixed?
import React from 'react';
import { Field, reduxForm } from 'redux-form';
import { Input, Button } from 'reactstrap';
const validate = values => {
const errors = {};
if (!values.question) { // this is just an example of what I am trying to do, validation does not work
errors.question = 'Required';
} else if (values.question.length < 15) {
errors.question = 'Must be 15 characters or more';
}
return errors;
};
const renderField = ({ input, label, type, meta: { touched, error } }) => (
<div>
<label>{label}</label>
<div>
<Input {...input} type={type} />
{touched && (error && <span>{error}</span>)}
</div>
</div>
);
const renderQuestions = questions => {
return questions.map(question => {
return (
<Field key={question.id} name={question.prompt} type="textarea" component={renderField} label={question.prompt} />
);
});
};
const QuizStepForm = props => {
const { handleSubmit, pristine, reset, submitting, questions } = props;
return (
<form onSubmit={handleSubmit}>
<Field name="username" type="textarea" component={renderField} label="username" />
{renderQuestions(questions)}
<div>
<br />
<Button color="primary" style={{ margin: '10px' }} type="submit" disabled={submitting}>
Submit
</Button>
<Button type="button" disabled={pristine || submitting} onClick={reset}>
Clear Values
</Button>
</div>
</form>
);
};
export default reduxForm({
form: 'quizStepForm',
validate
})(QuizStepForm);
Your validation function assumes there is one field named "question." But your code creates a set of fields whose name is set by {question.prompt}. If you stick with this implementation, your validation code will need to know about all the question.prompt array values and check values[question.prompt] for each one, then set errors[question.prompt] for any failures. That would probably work, though it seems like a suboptimal design.
This might be a good use case for a FieldArray. In FieldArrays, the validation function is called for you on each field; your validation code doesn't have to know the names of all the fields.