Can't convert input element to re-usable component in react js - javascript

I created a login form and now I want to convert my input fields to re- usable component. I created separate common input.jsx file. This is input.jsx file's code.
import React from "react";
const Input = ({ name, label, value, onChange }) => {
return (
<div className="form-group">
<label htmlFor={name}>{label}</label>
<input
value={value}
onChange={onChange}
id={name}
name={name}
type="text"
className="form-control"
/>
</div>
);
};
export default Input;
and imported it to my loginForm.jsx. Here is my loginForm.jsx render method
handleChange = ({ currentTarget: input }) => {
const account = { ...this.state.account };
account[input.name] = input.value;
this.setState({ account });
};
render() {
const { account } = this.state;
return (
<div>
<h1>Login</h1>
<form onSubmit={this.handleSubmit}>
<Input
name="username"
value={account.username}
label="Username"
onChange={this.handleChange}
/>
<Input
name="password"
value={account.password}
label="Password"
onChange={this.handleChange}
/>
<button className="btn btn-primary">Login</button>
</form>
</div>
);
}
But after adding below code to my loginForm.jsx,
<Input
name="username"
value={account.username}
label="Username"
onChange={this.handleChange}
/>
code and deleted previous code ,
<div className="form-group">
<label htmlFor="username">Username</label>
<input
value={account.username}
name="username"
onChange={this.handleChange}
ref={this.username}
id="username"
type="text"
className="form-control"
/>
</div>
suddenly my login page not loading.(Empty page).
My login page's console showing below error.
The above error occurred in the <LoginForm> component:
at LoginForm (http://localhost:3000/main.5d4e82bfe117bc198b43.hot-update.js:27:5)
at Route (http://localhost:3000/static/js/bundle.js:54444:5)
at Switch (http://localhost:3000/static/js/bundle.js:54739:5)
at main
at App
at Router (http://localhost:3000/static/js/bundle.js:54612:5)
at BrowserRouter (http://localhost:3000/static/js/bundle.js:53870:5)
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.

Related

How to make a form with just one input tag but multiple input fields with the use of map, keys and id

I created a form using this code using useState. But the issue is i want to make a code with lesser line and i want to create it with the use of keys and ids that uses only one single input tag in the code instead of multiple input tags. the code is mentioned below:
import './App.css';
import { useState } from "react";
function App() {
const [inputFields, setInputFields] = useState([
{
name:'',
email:'',
username:'',
password:'',
confirm:'',
mobile:''
}
])
const handelFormChange = (index, event) => {
let data = [...inputFields]
data[index][event.target.name] = event.target.value;
setInputFields(data);
}
const submit = (e) => {
e.preventDefault();
console.log(inputFields)
}
return (
<div className="App">
<form className="bg-light" onSubmit={submit}>
{/* <div className="form-group"> */}
{inputFields.map((input, index) => {
return(
<div key={index} className="form-group">
<label className="font-weight-regular"> Name </label>
<input type="name" name='name' required value={input.name} onChange={event => handelFormChange(index,event)}></input>
<label className="font-weight-regular"> Email </label>
<input type="text" name='email' required value={input.email} onChange={event => handelFormChange(index,event)}></input>
<label className="font-weight-regular"> Username </label>
<input type="text" name='username' required value={input.username} onChange={event => handelFormChange(index,event)}></input>
<label className="font-weight-regular"> Password </label>
<input type="password" name='password' required value={input.password} onChange={event => handelFormChange(index,event)}></input>
<label className="font-weight-regular"> Confirm Password </label>
<input type="password" name='confirm' required value={input.confirm} onChange={event => handelFormChange(index,event)}></input>
<label className="font-weight-regular"> Mobile Number </label>
<input type="text" name='mobile' required value={input.mobile} onChange={event => handelFormChange(index,event)}></input>
</div>
)
})}
<button onClick={submit}>Submit</button>
</form>
</div>
)
}
export default App;
So i tried to mention the make a form and i am a fresher in this field so i dont know how to make a form with single input field but i have the requirement of this code
It's possible to use a single input field to create a form with multiple inputs by using the 'name' attribute on the input field. The name attribute allows you to identify which input field the data is coming from when the form is submitted.

Create a reusable Form Input component with React & TypeScript

How can I define input attributes in typescript? I have an AddUser Component and TextInput Component, I want to import the TextInput component inside the AddUser component and then pass props to the TextInput component.
AddUser.tsx
import Button from '../Shared/Form/Button/Button';
import Form from '../Shared/Form/Form';
import TextArea from '../Shared/Form/TextArea/TextArea';
import TextInput from '../Shared/Form/TextInput/TextInput';
const AddUser = () => {
return (
<div>
<h1>Add User</h1>
<Form method="post" className={'user-form'}>
<TextInput label={'Name'} type="text" name="name" required />
<TextInput label={'Email'} type="email" name="email" required />
<TextInput label={'Country'} type="text" name="country" required />
<TextInput label={'Phone'} type="text" name="phone" required />
<TextArea label={'Address'} name="address" cols="30" rows="4" />
<div className="form-button">
<Button type={'submit'} className={'btn-add'}>
Add
</Button>
<Button type={'submit'} className={'btn-close'}>
Cancel
</Button>
</div>
</Form>
</div>
);
};
export default AddUser;
TextInput.tsx
const TextInput = ({ className, label, ...rest }: { className: string; label: string }) => {
return (
<div className={`${className} form-field`}>
<label htmlFor={label}>{label}</label>
<input {...rest} />
</div>
);
};
export default TextInput;
You can extend HTMLProps which comes with react:
import { HTMLProps } from "react";
interface MyCOmponentProps extends HTMLProps<HTMLInputElement> {
{...}
}
This is how we can make a reusable component. maybe I missed something in onChangeAction according to type script. please let me know me in the comment section so that i can help you better
Example codesandbox
const AddUser = () => {
return (
<div>
<h1>Add User</h1>
<form method="post" className={"user-form"}>
<TextInput
label={"Name"}
placeholder="Name"
type="text"
name="name"
required
/>
<TextInput
label={"Email"}
placeholder="Email"
type="email"
name="email"
required
/>
<TextInput
label={"Country"}
placeholder="Country"
type="text"
name="country"
required
/>
<TextInput
label={"Phone"}
placeholder="Phone"
type="text"
name="phone"
required
/>
</form>
</div>
);
};
export default AddUser;
const TextInput = ({
className,
label,
value,
type,
onChangeAction,
placeholder
}) => {
return (
<div className={`${className} form-field`}>
<label htmlFor={label}>{label}</label>
<input
placeholder={placeholder || "Text"}
type={type || "text"}
value={value}
onChange={onChangeAction}
/>
</div>
);
};

How can Prompt be used to stop navigation on formik dirty state

I'm using formik in a reactjs project, and I want to use Prompt from react-router to open a notification before a user leaves and loses changes made to their submission.
I'd expected something like this to work:
<Prompt
when={formik.dirty}
message="You have unsaved changes. Are you sure you want to leave?"
/>
My formik block looks like this:
const formik = useFormik({
initialValues: {
<values>
},
enableReinitialize: true,
validate,
onSubmit: values => {
<submit functional stuff>
}
});
And my form is something like this:
<form id="myForm" onSubmit={formik.handleSubmit}>
<div className="row">
<div className="form-group">
<label htmlFor="name">Name</label>
<input
id="name"
type="text"
onChange={formik.handleChange}
value={formik.values.name}
className="form-control"
placeholder="Enter name"
disabled={isDisabled}
/>
{formik.errors.name ? <div className="text-danger">{formik.errors.name}</div> : null}
</div>
<div className="form-group">
<label htmlFor="subject">Subject</label>
<input
id="subject"
type="text"
onChange={formik.handleChange}
value={formik.values.subject}
className="form-control"
placeholder="Email subject"
disabled={isDisabled}
/>
{formik.errors.subject ? <div className="text-danger">{formik.errors.subject}</div> : null}
</div>
</div>
</form>
but it appears that formik.dirty is either not defined or it's not seen as true (despite making changes to the form).
How would I properly use the dirty prop to trigger the Prompt?
I am not sure what kind of setup you have, but I created a PoC with routing which has two tabs (links) for navigation and I am using prompt on tab with formik form component.
import React from "react";
import { Prompt } from "react-router-dom";
import { useFormik } from "formik";
const MyForm = () => {
const formik = useFormik({
initialValues: {
name: "",
subject: ""
},
enableReinitialize: true,
onSubmit: (values) => {
alert(JSON.stringify(values, null, 2));
},
onChange: (e) => {
console.log(e);
}
});
return (
<div>
<Prompt
when={!!formik.dirty}
message={(location) =>
`Are you sure you want to go to ${location.pathname}`
}
/>
<form id="myForm" onSubmit={formik.handleSubmit}>
<div className="row">
<div className="form-group">
<label htmlFor="name">Name</label>
<input
id="name"
type="text"
onChange={formik.handleChange}
value={formik.values.name}
className="form-control"
placeholder="Enter name"
/>
{formik.errors.name ? (
<div className="text-danger">{formik.errors.name}</div>
) : null}
</div>
<div className="form-group">
<label htmlFor="subject">Subject</label>
<input
id="subject"
type="text"
onChange={formik.handleChange}
value={formik.values.subject}
className="form-control"
placeholder="Email subject"
/>
{formik.errors.subject ? (
<div className="text-danger">{formik.errors.subject}</div>
) : null}
</div>
{formik.dirty && <button tye="submit">Save</button>}
</div>
</form>
</div>
);
};
export default MyForm;
take a look the this codesandbox.

Where does redux-form's `meta : { touched, error }` live? Could I access it when not contained by renderField scope?

The redux-form documentation advises me to render my input and submit-validation errors like this.
const renderField = ({ input, placeholder, className, type, meta: { touched, error } }) => (
<div>
<input {...input} className={className} placeholder={placeholder} type={type}/>
{touched && error && <span><font color="red">{error}</font></span>}
</div>
)
Inside render(){return(<form> </form>)} you are then supposed to create your inputs like this (note component={renderField} in the next code line):
<Field type="password" placeholder="password" className="form-control" component={renderField} name="password"/>
I wanted to customize this in order to fit it better into my own work. But I cannot seem to find a way to target touched and error unless I place the component in renderField, I guess I am still missing some vital knowledge. Where are these meta: {touched, error} properties going exactly and if I can access them somewhere?
Below is my entire container file for your reference.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm } from "redux-form"
import Title from "../components/Title.js"
const renderField = ({ input, placeholder, className, type, meta: { touched, error } }) => (
<div className={"" + touched && error && "input_error_border"}>
<input {...input} className={className} placeholder={placeholder} type={type}/>
{touched && error && <span><font color="red">{error}</font></span>}
</div>
)
class RegisterForm extends React.Component {
constructor(props) {
super(props);
this.props = props;
}
is_registered(){
if(this.props.user.server && this.props.user.insert){
return (<div>
<p>Thank you for registering {this.props.user.first_name}</p>
<p>You will recieve an email with further instructions shortly.</p>
</div>)
}else{
return <div></div>
}
}
render() {
const { handleSubmit } = this.props
console.log(this.props)
return (
<form onSubmit={ handleSubmit } className="box-sizing mx-auto max_vertical_form_400">
<Title innerH="Register New User"/>
<div className="input-group-btn">
{this.is_registered()}
</div>
<div className="form-group">
<Field type="text" placeholder="first name" className="form-control" component={renderField} name="first_name" />
<Field type="text" placeholder="last name" className="form-control" component={renderField} name="last_name" />
</div>
<div className="form-group">
<Field type="text" placeholder="email" className="form-control" component={renderField} name="email"/>
</div>
<div className="form-group">
<Field type="text" placeholder="company" className="form-control" component={renderField} name="company"/>
<Field type="text" placeholder="department" className="form-control" component={renderField} name="department"/>
</div>
<div className="form-group">
<Field type="password" placeholder="password" className="form-control" component={renderField} name="password"/>
<Field type="password" placeholder="repeat password" className="form-control" component={renderField} name="password_repeated"/>
</div>
<div className="input-group-btn">
<button type="submit" className="btn btn-primary">Submit</button>
</div>
{/* <RegisterFormContainer />
<ThemeContainer /> */}
</form>
);
}
}
function validate(values){
const errors= {};
if(!values.password) errors.password = "missing password";
if (!values.email) {
errors.email = 'Required'
} else if (!/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
errors.email = 'Invalid email address'
}
return errors;
}
RegisterForm = reduxForm({
form: "register_user_form",
validate
})(RegisterForm)
function mapStateToProps({ user }) {
return { user };
}
export default RegisterForm = connect(mapStateToProps, null)(RegisterForm)
You can use redux-form selectors, specifically getFormMeta to know which fields are dirty or touched and getFormSyncErrors to know the fields having errors.
In your code, you need to change to import the selectors
import { getFormMeta, getFormSyncErrors } from 'redux-form';
add it to your mapStateToProps which might look like this:
function mapStateToProps(state) {
return {
user: state.user,
metaForm: getFormMeta('register_user_form')(state),
formSyncErrors: getFormSyncErrors('register_user_form')(state),
};
}
and then you can reference in your component with this.props.metaForm and this.props.formSyncErrors

Simple Redux-Form Component returns undefined

Just created this form as part of a tutorial. However, my console log is giving me undefined whenever I submit the form. What am I doing wrong?
import React, {Component} from 'react';
import { reduxForm } from 'redux-form';
class Signin extends Component {
handleFormSubmit({email, password}) {
console.log(email); // this gives me 'undefined'
}
render() {
const {handleSubmit, fields: {email, password}} = this.props;
return (
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
<fieldset className="form-group">
<label>Email:</label>
<input {...email} className="form-control" />
</fieldset>
<fieldset className="form-group">
<label>Password:</label>
<input {...password} className="form-control" />
</fieldset>
<button action="submit" className="btn btn-primary">Sign in</button>
</form>
);
}
}
export default reduxForm({
form: 'signin',
fields: ['email', 'password']
})(Signin);
This is due to an update to redux-form.
Rather than importing the values of {email, password} from this.props we instead use a Field component imported from redux-form.
import { Field, reduxForm } from 'redux-form';
Then using it in place of the input tag you have:
<fieldset className="form-group">
<label>Email:</label>
<Field
name="email"
className="form-control"
component="input"
type="text"
placeholder="Email"
/>
</fieldset>
The connection of this input to redux-form now comes from the name property instead of extracting it from this.props
Important to note: that the name MUST be the same as the name in the fields: [] array defined in the reduxForm:
export default reduxForm({
form: 'signin',
fields: ['email', 'password']
})(Signin);
Building on this, if you want to customise the component that the Field uses, you can define your own custom component pretty easily.
Instead of supplying a string to Field's component property, you can define a function:
<div className="form-group">
<label>Last name:</label>
<Field type="text" className="form-control" placeholder="Smith" name="lastName"
component={textField} />
</div>
textField is imported from another file: import {textField} from '../../redux_form_elements';
And textField is as follows:
import React from 'react';
export const textField = (field) => (
<div>
<input className="form-control" {...field.input} type={field.type} placeholder={field.placeholder} />
{field.meta.touched && field.meta.error &&
<label id="basic-error" className="validation-error-label">This field is required.</label>
}
</div>
);
This looks like the Grider Tutorial. I fixed it this way, with a renderInput function which takes the field. Not sure if this is useful for you.
import React, {Component} from 'react';
import { reduxForm } from 'redux-form';
import { Field } from 'redux-form';
const renderInput = field => (
<div>
<input {...field.input} type={field.type} className="form-control" />
{field.meta.touched && field.meta.error}
<span>{field.meta.error}</span>
</div>
);
class Signin extends Component {
handleFormSubmit({email, password}) {
console.log(email);
console.log("Hi");
}
render() {
const {handleSubmit, fields: {email, password}} = this.props;
return (
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
<fieldset className="form-group">
<label>Email</label>
<Field
name="email"
component={renderInput}
type="text" />
</fieldset>
<fieldset className="form-group">
<label>Password</label>
<Field
name="password"
component={renderInput}
type="text" />
</fieldset>
<button action="submit" className="btn btn-primary">Sign in</button>
</form>
);
}
}
export default reduxForm({
form: 'signin',
fields: ['email', 'password']
})(Signin);

Categories

Resources