Why my handleChange is not being invoked? MaterialUI - javascript

Is there something wrong with the TextField component from Material UI that I'm missing?
My handleChange simply is not getting invoked.
I can type in value in the inputs, but state of the component is not changing.
Here is the Auth component:
const Auth = () => {
const [formData, setFormData] = useState(initialFormState)
const handleSubmit = (e) => {
e.preventDefault();
console.log(formData);
}
const handleChange = (e) => {
setFormData({
...formData,
[e.target.name]: e.target.value
})
}
return (
<Container component="main" maxWidth="xs">
<Paper className={classes.paper} elevation={3}>
<form className={classes.form} onSubmit={handleSubmit}>
<Grid container spacing={2}>
<Input name="email" label="e-mail" handleChange={handleChange} type="email" />
<Input name="password" label="password" handleChange={handleChange} type={showPassword ? "text" : "password"} handleShowPassword={handleShowPassword} />
</Grid>
<Button type="submit" variant="contained" color="primary" className={classes.submit} fullWidth>
{isSignup ? "Sign up" : "Sign in"}
</Button>
</form>
</Paper>
</Container>
);
}
And the input component which is inside Auth:
const Input = ({ name, half, handleChange, type, label, autoFocus, handleShowPassword }) => {
return (
<>
<Grid item xs={12} sm={half ? 6 : 12}>
<TextField
name={name}
handleChange={handleChange}
variant="outlined"
required
fullWidth
label={label}
autoFocus={autoFocus}
type={type}
InputProps={name === "password" ? {
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={handleShowPassword}>
{type === "password" ? <Visibility/> : <VisibilityOff/>}
</IconButton>
</InputAdornment>
)
} : null}
/>
</Grid>
</>
);
}

You've to change the input attribute from handleChange to onChange
<Input name="email" label="e-mail" onChange={handleChange} type="email" />
<Input name="password" label="password" onChange={handleChange} type={showPassword ? "text" : "password"} handleShowPassword={handleShowPassword} />

Related

How to toggle show and hide password for multiple field in React

I want to toggle the show and hide password for the input fields.
It can work on single input, but I don't know how to do it for multiple input field
I want that on clicking input icon it will toggle for the show/hide password
It should work individually for each input field.
for example if I click on current password input field icon then it will only show/hide password for this input field.
const App = () => {
const [values, setValues] = React.useState({
password: "",
showPassword: false,
});
const handleClickShowPassword = () => {
setValues({ ...values, showPassword: !values.showPassword });
};
const handlePasswordChange = (prop) => (event) => {
setValues({ ...values, [prop]: event.target.value });
};
return (
<div
style={{
marginLeft: "30%",
}}
>
<h4>Change your password</h4>
<InputLabel htmlFor="standard-adornment-password">
Current password
</InputLabel>
<Input
type={values.showPassword ? "text" : "password"}
onChange={handlePasswordChange("password")}
value={values.password}
endAdornment={
<InputAdornment position="end">
<IconButton
onClick={handleClickShowPassword}
>
{values.showPassword ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment>
}
<InputLabel htmlFor="standard-adornment-password">
New password
</InputLabel>
<Input
type={values.showPassword ? "text" : "password"}
onChange={handlePasswordChange("password")}
value={values.password}
endAdornment={
<InputAdornment position="end">
<IconButton
onClick={handleClickShowPassword}
>
{values.showPassword ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment>
}
<InputLabel htmlFor="standard-adornment-password">
Confirm password
</InputLabel>
<Input
type={values.showPassword ? "text" : "password"}
onChange={handlePasswordChange("password")}
value={values.password}
endAdornment={
<InputAdornment position="end">
<IconButton
onClick={handleClickShowPassword}
>
{values.showPassword ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment>
}
/>
</div>
);
};
Because you are using one boolean to check 3 fields password. To fix, you just update showPassword to store string like this:
const handleClickShowPassword = (fieldName) => {
setValues({ ...values, showPassword: fieldName === values.showPassword ? "" : fieldName});
};
...
type={values.showPassword === "currentPassword" ? "text" : "password"}
onClick={() => handleClickShowPassword("currentPassword")}
{values.showPassword === "currentPassword" ? <Visibility /> : <VisibilityOff />}
...
...
type={values.showPassword === "newPassword" ? "text" : "password"}
onClick={() => handleClickShowPassword("newPassword")}
{values.showPassword === "newPassword" ? <Visibility /> : <VisibilityOff />}
...
...
type={values.showPassword === "confirmPassword" ? "text" : "password"}
onClick={() => handleClickShowPassword("confirmPassword")}
{values.showPassword === "confirmPassword" ? <Visibility /> : <VisibilityOff />}
...
I was able to come up with this. Let me know if it helps
const App = () => {
const [values, setValues] = React.useState({
currentPassword:{show:false,password:'' "},
newPassword:{show:false,password:'' "},
confirmPassword{show:false,password:'' "}
});
const handleClickShowPassword = (prop) =>{
Input = values[props]
setValues({ ...values, [prop]: {show:!Input.show, password:Input.password} });
};
const handlePasswordChange = (prop,value) => {
Input = values[props]
setValues({ ...values, [prop]: {show:Input.show,
password:value} });
};
return (
<div
style={{
marginLeft: "30%",
}}
>
<h4>Change your password</h4>
<InputLabel htmlFor="standard-adornment-password">
Current password
</InputLabel>
<Input
type={values.currentPassword.show ? "text" : "password"}
onChange={({target})=>handlePasswordChange("currentPassword",target.value)}
value={values.currentPassword.password}
endAdornment={
<InputAdornment position="end">
<IconButton
onClick={handleClickShowPassword}
>
{values.currentPassword.show ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment>
}
<InputLabel htmlFor="standard-adornment-password">
New password
</InputLabel>
<Input
type={values.newPassword.show ? "text" : "password"}
onChange={({target})=>handlePasswordChange("newPassword",target.value)}
value={values.newPassword.password}
endAdornment={
<InputAdornment position="end">
<IconButton
onClick={handleClickShowPassword}
>
{values.newPassword.show ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment>
}
<InputLabel htmlFor="standard-adornment-password">
Confirm password
</InputLabel>
<Input
type={values.confirmPassword.show ? "text" : "password"}
onChange={({target})=>handlePasswordChange("confirmPassword",target.value)}
value={values.confirmPassword.password}
endAdornment={
<InputAdornment position="end">
<IconButton
onClick={handleClickShowPassword}
>
{values.confirmPassword.show ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment>
}
/>
</div>
);
};
Could you do something like that ?
const Input = (props) => {
const [showPassword, setShowPassword] = React.useState(false)
return (
<div>
<input type={showPassword ? "text" : "password"} {...props} />
<button onMouseDown={(e) => {
e.preventDefault()
setShowPassword(!showPassword)
}}>{showPassword ? 'Hide' : 'Show'} password</button>
</div>
)
}
const reducer = (state, changes) => {
return {
...state,
...changes
}
}
const App = () => {
const [values, setValues] = React.useReducer(reducer, {
currentPassword: "",
newPassword: "",
confirmPassword: ""
});
return (
<div style={{ marginLeft: "30%" }}>
<h4>Change your password</h4>
<label htmlFor="current-password">Current password</label>
<Input id="password-input" onChange={({target}) => {
return setValues({currentPassword: target.value})
}} />
<label htmlFor="new-password">New password</label>
<Input id="new-password" onChange={({target}) => {
return setValues({newPassword: target.value})
}} />
<label htmlFor="confirm-password">Confirm password</label>
<Input id="confirm-password" onChange={({target}) => {
return setValues({confirmPassword: target.value})
}} />
<h4>Form content</h4>
<ul>
<li>currentPassword : {values.currentPassword}</li>
<li>newPassword : {values.newPassword}</li>
<li>confirmPassword : {values.confirmPassword}</li>
</ul>
</div>
);
};
ReactDOM.render(<App />, document.querySelector("#app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="app"></div>

TypeError: Cannot read property 'style' of null on TextareaAutosize

I am having trouble with unit snapshot testing and cant figure why.
I am getting the following error:
enter image description here
The component is actually a Form with 3 fields: Full name, Email and Note. There is also a checkbox which controls the Email and Note field in case we dont want to put that information in the form.
For the Note I am using TextareaAutosize, so that the text area is re sizable (due to requirements).
const [missingInfo, setMissingInfo] = useState(false);
const initialValues = {
fullName: '',
email: '',
note: '',
};
return (
<Formik
initialValues={initialValues}
validationSchema={object({
fullName: string().required('Must contain a full name'),
})}
onSubmit={(values, { setSubmitting }) => {
setSubmitting(true);
...
}}
>
<Form>
<Typography>Title</Typography>
<HashLink
to={{ hash: '#xxx' }}
onClick={() => toggleHash()}
>
Changed your mind?
</HashLink>
<Typography>Full name</Typography>
<Field name="fullName">
{({ field }: FieldProps) => (
<TextField
id="fullName"
variant="outlined"
type="text"
fullWidth
{...field}
/>
)}
</Field>
<ErrorMessage name="fullName">
{msg => <div className={styles.errorMessage}>{msg}</div>}
</ErrorMessage>
<FormControlLabel
control={
<Checkbox
checked={missingInfo}
onChange={() => setMissingInfo(!missingInfo)}
name="missingInfo"
/>
}
label="No info?"
/>
{!missingInfo && (
<>
<Typography>Email</Typography>
<Field name="email">
{({ field }: FieldProps) => (
<TextField
id="email"
variant="outlined"
type="text"
fullWidth
{...field}
/>
)}
</Field>
<Typography>Note</Typography>
<Field name="note">
{({ field }: FieldProps) => (
<TextareaAutosize
rowsMin={3}
className={styles.textArea}
{...field}
/>
)}
</Field>
</>
)}
<Button
variant="contained"
type="submit"
>
Apply
</Button>
</Form>
</Formik>
)
The error is as I said in the snapshot test:
test('matches snapshot', () => {
const tree = renderer
.create(
<BrowserRouter>
<MyComponent {...props} />
</BrowserRouter>,
)
.toJSON();
expect(tree).toMatchSnapshot();
});
Can someone please help?

How to append inputs with formik in react

So this may be something simple but I'm hitting a roadblock. I want to take in a form input but can't seem to figure out how to append the value correctly so its appending string is captured.
I'm using Formik with yup in react.
<InputGroup>
<Field
id="appended-input"
name="domain_url"
type="text"
value={values.domain_url}
className={
"form-control" +
(errors.domain_url && touched.domain_url
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="domain_url"
component="div"
className="invalid-feedback"
/>
<InputGroupAddon addonType="append">
.localhost
</InputGroupAddon>
</InputGroup>
Any help would be appreciated. I just want to get the .localhost to be automatically added to the input items. for this field. I thought I could do something like value=({values.domain_url} + ".localhost") but that didn't seem to work and as you may already tell I am very new to javascript.
Thank you!
Full code below, I'm also having issues with datepicker displaying within the formik state, and then there's how to even get the values to push to my getTenant(function) to be passed to my api.
static propTypes = {
addTenant: PropTypes.func.isRequired,
};
onSubmit = (values) => {
values.preventDefault();
this.props.addTenant(values);
};
render() {
const {
domain_url,
schema_name,
name,
config,
} = this.state;
const TenantSchema = Yup.object().shape({
domain_url: Yup.string()
.max(255, "Must be shorter than 255 characters")
.required("Client URL header is required"),
schema_name: Yup.string()
.max(255, "Must be shorter than 255 characters")
.required("Client db name is required"),
name: Yup.string()
.max(255, "Must be shorter than 255 characters")
.required("Client name is required"),
});
return (
<div className={s.root}>
<Formik
initialValues={{
domain_url: "",
schema_name: "",
client_name: "",
config: [
{
date: "",
Tenant_description: "",
},
],
}}
// validationSchema={TenantSchema} this is commented off because it breaks
submittions
onSubmit={(values, { setSubmitting, resetForm }) => {
setSubmitting(true);
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
resetForm();
setSubmitting(false);
}, 100);
}}
//onSubmit={onSubmit}
>
{({
values,
errors,
status,
touched,
handleBlur,
handleChange,
isSubmitting,
setFieldValue,
handleSubmit,
props,
}) => (
<FormGroup>
<Form onSubmit={handleSubmit}>
<legend>
<strong>Create</strong> Tenant
</legend>
<FormGroup row>
<Label for="normal-field" md={4} className="text-md-right">
Show URL
</Label>
<Col md={7}>
<InputGroup>
<Field
id="appended-input"
name="domain_url"
type="text"
value={values.domain_url}
onSubmit={(values) => {
values.domain_url = values.domain_url + ".localhost";
}} //this isn't working
className={
"form-control" +
(errors.domain_url && touched.domain_url
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="domain_url"
component="div"
className="invalid-feedback"
/>
<InputGroupAddon addonType="append">
.localhost
</InputGroupAddon>
</InputGroup>
</Col>
</FormGroup>
<FormGroup row>
<Label for="normal-field" md={4} className="text-md-right">
Database Name
</Label>
<Col md={7}>
<Field
name="schema_name"
type="text"
className={
"form-control" +
(errors.schema_name && touched.schema_name
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="schema_name"
component="div"
className="invalid-feedback"
/>
</Col>
</FormGroup>
<FormGroup row>
<Label for="normal-field" md={4} className="text-md-right">
Name
</Label>
<Col md={7}>
<Field
name="name"
type="text"
className={
"form-control" +
(errors.name && touched.name
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="name"
component="div"
className="invalid-feedback"
/>
</Col>
</FormGroup>
<FieldArray
name="config"
render={(arrayHelpers) => (
<div>
{values.config.map((config, index) => (
<div key={index}>
<FormGroup row>
<Label
md={4}
className="text-md-right"
for="mask-date"
>
Tenant Description
</Label>
<Col md={7}>
<TextareaAutosize
rows={3}
name={`config.${index}.tenant_description`}
id="elastic-textarea"
type="text"
onReset={values.event_description}
placeholder="Quick description of tenant"
onChange={handleChange}
value={values.tenant_description}
onBlur={handleBlur}
className={
`form-control ${s.autogrow} transition-height` +
(errors.tenant_description &&
touched.tenant_description
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="tenant_description"
component="div"
className="invalid-feedback"
/>
</Col>
</FormGroup>
<FormGroup row>
<Label
for="normal-field"
md={4}
className="text-md-right"
>
Date
</Label>
<Col md={7}>
<DatePicker
tag={Field}
name={`config.${index}.date`}
type="date"
selected={values.date}
value={values.date}
className={
"form-control" +
(errors.date&& touched.date
? " is-invalid"
: "")
}
onChange={(e) =>
setFieldValue("date", e)
}
/>
<ErrorMessage
name="date"
component="div"
className="invalid-feedback"
/>
</Col>
</FormGroup>
</div>
))}
</div>
)}
/>
<div className="form-group">
<button
type="submit"
disabled={isSubmitting}
className="btn btn-primary mr-2"
>
Save Tenant
</button>
<button type="reset" className="btn btn-secondary">
Reset
</button>
</div>
</Form>
<Col md={7}>{JSON.stringify(values)}</Col>
</FormGroup>
)}
</Formik>
</div>
);
}
}
export default connect(null, { addTenant })(TenantForm);
You could use setFieldValue if you want to customize the value
https://jaredpalmer.com/formik/docs/api/formik#setfieldvalue-field-string-value-any-shouldvalidate-boolean--void
You can add onChange
<Field
id="appended-input"
name="domain_url"
type="text"
value={values.domain_url}
onChange={st => {
let value = st.target.value;
let suffix = ".localhost";
let index = value.indexOf(".localhost");
if (index > 0) {
suffix = "";
}
//add suffix 'localhost' if it is not already added
props.setFieldValue("domain_url", value + suffix);
}}
className={
"form-control" +
(errors.domain_url && touched.domain_url ? " is-invalid" : "")
}
/>;
But adding suffix is more preferable on onSubmit:
onSubmit = {(values, actions) => {
console.log('valuesbefore',values)
values.domain_url= values.domain_url+ ".localhost"
console.log('valuesafter',values)
this.props.addTenant(values);
};

React+JavaScript How to create an object in React with all the given fields

I have the following Sign-up page and I console log each time in the function called 'sign' once the Sign Up button is clicked. While each field is individually visible in the console, I need to make them appear as one object. Any help is appreciated, here is my code for the react function SignUp:
export default function SignUp() {
const [firstName, setFirstName] = React.useState("");
const [email, setEmail] = React.useState("");
const [password, setPassword] = React.useState("");
let sign = (event) => {
event.preventDefault();
console.log(firstName);
};
return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Sign up
</Typography>
<form className={classes.form} noValidate>
<Grid container spacing={2}>
<Grid item xs={12} sm={6}>
<TextField
autoComplete="fname"
name="firstName"
variant="outlined"
required
fullWidth
id="firstName"
label="First Name"
autoFocus
onChange={(event) => setFirstName(event.target.value)}
/>
</Grid>
<Grid item xs={12}>
<TextField
variant="outlined"
required
fullWidth
id="email"
label="Email Address"
name="email"
autoComplete="email"
onChange={(event) => setEmail(event.target.value)}
/>
</Grid>
<Grid item xs={12}>
<TextField
variant="outlined"
required
fullWidth
name="password"
label="Password"
type="password"
id="password"
autoComplete="current-password"
onChange={(event) => setPassword(event.target.value)}
/>
</Grid>
</Grid>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
onClick={sign}
>
Sign Up
</Button>
You can use useReducer to create a state object of your component like this:
import React, { useReducer } from 'react'
const initialState = {
firstName: '',
email: '',
password: '',
}
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'SET_FIRSTNAME':
return { ...state, firstName: action.payload }
case 'SET_EMAIL':
return { ...state, email: action.payload }
case 'SET_PASSWORD':
return { ...state, password: action.payload }
default:
return state
}
}
export default function SignUp() {
const [state, dispatch] = useReducer(reducer, initialState)
let sign = (event) => {
event.preventDefault();
console.log(state);
};
return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Sign up
</Typography>
<form className={classes.form} noValidate>
<Grid container spacing={2}>
<Grid item xs={12} sm={6}>
<TextField
autoComplete="fname"
name="firstName"
variant="outlined"
required
fullWidth
id="firstName"
label="First Name"
autoFocus
onChange={(event) => dispatch({ type: 'SET_FIRSTNAME', payload: event.target.value })}
/>
</Grid>
<Grid item xs={12}>
<TextField
variant="outlined"
required
fullWidth
id="email"
label="Email Address"
name="email"
autoComplete="email"
onChange={(event) => dispatch({ type: 'SET_EMAIL', payload: event.target.value })}
/>
</Grid>
<Grid item xs={12}>
<TextField
variant="outlined"
required
fullWidth
name="password"
label="Password"
type="password"
id="password"
autoComplete="current-password"
onChange={(event) => dispatch({ type: 'SET_PASSWORD', payload: event.target.value })}
/>
</Grid>
</Grid>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
onClick={sign}
>
Sign Up
</Button>
You can do something like this :
console.log({ firstName , email , password });

Force a disabled MI TextField to be clickable

I have a TextField that is disabled and I'm updating it with React Hooks useState by changing the value property of the TextField.
const [employee , setEmployee] = React.useState('')
<TextField
fullWidth
disabled
value={employee}
variant="outlined"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<BackupIcon onClick={handleClick}/>
</InputAdornment>
),
}}
/>
It's only clickable on the Icon BackupIcon.
How can we make it clickable all over the TextField ?
Have you tried putting the onClick like this:
handleClick: function(e) {
this.setState({
textFieldValue: e.target.value
});
},
<TextField fullWidth
disabled
value={employee}
variant="outlined"
onChange={handleClick}
>
<BackupIcon />
<TextField />

Categories