Input State not updating correctly - React - javascript

Every time I update the text of the input, it doesn't update. When i update the text a second time, the state updates to the text of the first update. I'm not sure what exactly is happening but I suspect it's a deep/shallow copy error I'm not seeing? Any help would be appreciated
const DropDownPrompt = (props) => {
//set type by passing hook through props
const [dropDownOptions, setDDoptions] = useState([{id : 1 , value : "Value 1"},{id : 2 , value : "Value 2"}]);
return (
<div className="mb-0 p-3">
<Row>
<Col>
<FormGroup>
<Label htmlFor="exampleSelect" style={{fontWeight : "bold"}}>Preview</Label>
<Input type="select" name="select" id="exampleSelect">
{dropDownOptions? dropDownOptions.map((ddO) =>
<option>{ddO.value}</option>
):null}
</Input>
</FormGroup>
</Col>
<Col>
<Label style={{fontWeight : "bold"}} >Options (at least 2)</Label>
{dropDownOptions? dropDownOptions.map((ddO, index) =>
<Row>
<FormGroup>
<Label check >
<Input key = {index } type="text" value = {ddO.value} bsSize="sm" onChange= { async (e) =>{
let carrier = [...dropDownOptions]
carrier[ddO.id - 1] = {...carrier[ddO.id - 1], value : e.target.value }
setDDoptions(carrier)
console.log(dropDownOptions)
}} />
</Label>
{ddO.id > 2 ?
<Button className="btn-wrapper--icon ml-2 btn-link btn-sm" >
<FontAwesomeIcon icon={['fas', 'minus-circle']} className="font-size-lg text-danger" />
</Button>:null}
</FormGroup>
</Row>
):null}
<Button className="btn-link" onClick = {() => {setDDoptions ([...dropDownOptions, {id : dropDownOptions.length + 1, value : `Value ${dropDownOptions.length + 1}`}])}} >
<span className="btn-wrapper--icon">
<FontAwesomeIcon icon={['fas', 'plus-circle']} className="font-size-lg" />
</span>
<span className="btn-wrapper--label">
Add another
</span> </Button>
</Col>
</Row>
</div>
)
}
export default (DropDownPrompt)

Related

How to enable/disable form elements in a list generated by a loop in ReactJS?

I have a loop in react to generate a list of input text and select, specifically two inputs and a select for each line
const getRow = (type, key, defaultValues = {}) => {
return (
<Row className="mb-3" key={type + key}>
<Col xs={3}>
<Form.Group>
<Form.Label>Name</Form.Label>
<Form.Control
name={`${type}name${key}`}
className={`${type}name`}
defaultValue={defaultValues ? defaultValues.name : null}
type="text"/>
</Form.Group>
</Col>
<Col>
<Form.Group>
<Form.Label>Values</Form.Label>
<Form.Control
name={`${type}value${key}`}
className={`${type}value`}
defaultValue={defaultValues ? defaultValues.value : null}
type="text"/>
</Form.Group>
</Col>
<Col xs={3}>
<Form.Group>
<Form.Label>Type</Form.Label>
<Form.Select
name={`${type}type${key}`}
className={`${type}type`}
defaultValue={defaultValues ? defaultValues.type : null}
>
<option value="single">Single</option>
<option value="multi">Multi</option>
<option value="date">Date</option>
</Form.Select>
</Form.Group>
</Col>
</Row>
What I am trying to do is to have the second input text (value) disabled when I select Date in the type select. The tricky point is to disable the one in the same line.
How can I do that please?
I don't know what data your key variables have? But i presume that they are unique as you named them key. You can create an object hold the state of the disabledElements like this:
{
textvalueMyCoolKey: true,
textValueMyThirdCoolKey: true
}
Then check if your disabled element is in that object. So it could look like this. You didn't post much of your code so I can't give you a component that works, but this tells the idea:
const [disabledElements, setDisabledElements] = useState({});
const setDisabledText = (e, key) => {
const value = e.target.value;
const disabled = value === 'date';
setDisabledElements({...disabledElements, [key]: disabled});
}
useEffect(() => {
}, [disabledElements]);
const getRow = (type, key, defaultValues = {}) => {
return (
<Row className="mb-3" key={type + key}>
<Col xs={3}>
<Form.Group>
<Form.Label>Name</Form.Label>
<Form.Control
name={`${type}name${key}`}
className={`${type}name`}
defaultValue={defaultValues ? defaultValues.name : null}
type="text"/>
</Form.Group>
</Col>
<Col>
<Form.Group>
<Form.Label>Values</Form.Label>
<Form.Control
name={`${type}value${key}`}
className={`${type}value`}
disabled={disabledElements[`${type}value${key}`]}
defaultValue={defaultValues ? defaultValues.value : null}
type="text"/>
</Form.Group>
</Col>
<Col xs={3}>
<Form.Group>
<Form.Label>Type</Form.Label>
<Form.Select
name={`${type}type${key}`}
className={`${type}type`}
onChange={(e) => setDisabledText(e, `${type}value${key}`)}
defaultValue={defaultValues ? defaultValues.type : null}
>
<option value="single">Single</option>
<option value="multi">Multi</option>
<option value="date">Date</option>
</Form.Select>
</Form.Group>
</Col>
</Row>
)
}
Also I presumed that you want to remove the disabled state from the text field if the user deselects the date option.

Cannot enter value into MDEditor

When I run the program, I can't enter a value in the input, help me
const FormCommnent = ({ onChange, onSubmit, value }) => (
<div className="form-comment">
<MDEditor
value={value}
onChange={onChange}
name="content"
rows={4}
autoFocus={0}
preview='edit'
/>
<MDEditor.Markdown source={value} />
<Button htmlType="submit" onClick={onSubmit} >
Add Comment
</Button>
</div>
);

Binding input type using refs in reactjs

I am a beginner in reactJS and would like to ask a simple question. I am following some tutorials and I really don't know why this wont work on mine. I know my question is very simple since I am a beginner. Please have a patience with my question.
I have this code from a function component form:
function Login() {
const emailInputRef = useRef();
const passwordInputRef = useRef();
function submitHandler(e) {
e.preventDefault();
const emailInput = emailInputRef.current.value;
const passwordInput = passwordInputRef.current.value;
console.log(emailInput);
}
return (
<>
<Col lg="5" md="7">
<Card className="bg-secondary shadow border-0">
<CardBody className="px-lg-5 py-lg-5">
<div className="text-center text-muted mb-4">
<small>Sign in</small>
</div>
<Form role="form" onSubmit={submitHandler}>
<FormGroup className="mb-3">
<InputGroup className="input-group-alternative">
<InputGroupAddon addonType="prepend">
<InputGroupText>
<i className="ni ni-email-83" />
</InputGroupText>
</InputGroupAddon>
<Input
placeholder="Email"
type="email"
autoComplete="new-email"
ref={emailInputRef}
/>
</InputGroup>
</FormGroup>
<FormGroup>
<InputGroup className="input-group-alternative">
<InputGroupAddon addonType="prepend">
<InputGroupText>
<i className="ni ni-lock-circle-open" />
</InputGroupText>
</InputGroupAddon>
<Input
placeholder="Password"
type="password"
autoComplete="new-password"
ref={passwordInputRef}
/>
</InputGroup>
</FormGroup>
<div className="custom-control custom-control-alternative custom-checkbox">
<input
className="custom-control-input"
id=" customCheckLogin"
type="checkbox"
/>
<label
className="custom-control-label"
htmlFor=" customCheckLogin"
>
<span className="text-muted">Remember me</span>
</label>
</div>
<div className="text-center">
<Button className="my-4" color="primary" type="submit">
Sign in
</Button>
</div>
</Form>
</CardBody>
</Card>
<Row className="mt-3">
<Col xs="6">
<a
className="text-light"
href="#pablo"
onClick={(e) => e.preventDefault()}
>
<small>Forgot password?</small>
</a>
</Col>
<Col className="text-right" xs="6">
<a
className="text-light"
href="#pablo"
onClick={(e) => e.preventDefault()}
>
<small>Create new account</small>
</a>
</Col>
</Row>
</Col>
</>
);
}
I want to bind my input type from my submitHandler and log the values of the input types. I followed a tutorial using refs and I don't know if I made something wrong about it since I was a beginner. When I logged the values it gives me undefined values.
To get the current input reference use innerRef property instead of ref:
<Input
placeholder="Email"
type="email"
autoComplete="new-email"
innerRef={emailInputRef}
/>
const emailInput = emailInputRef.current.value;
ref will only get you a reference to the Input component, innerRef will get you a reference to the DOM input.
You have a few options for that. You can use the useRef React hook or the state stored using the useState React Hook. In both cases you need to import the related hook first. You could use both if you want. You could also use a custom hook with the useReducer React hook.
In your case it would be something like these:
Option # 1: Using useRef
import {useRef} from "react";
function Login() {
const emailInputRef = useRef();
const passwordInputRef = useRef();
function submitFormHandler(event) {
event.preventDefault();
const email = emailInputRef.current.value;
const password = passwordInputRef.current.value;
console.log(`email = ${email} & password = ${password}`);
}
return (
<>
<Col lg="5" md="7">
<Card className="bg-secondary shadow border-0">
<CardBody className="px-lg-5 py-lg-5">
<div className="text-center text-muted mb-4">
<small>Sign in</small>
</div>
<Form role="form" onSubmit={submitFormHandler}>
<FormGroup className="mb-3">
<InputGroup className="input-group-alternative">
<InputGroupAddon addonType="prepend">
<InputGroupText>
<i className="ni ni-email-83"/>
</InputGroupText>
</InputGroupAddon>
<Input
placeholder="Email"
type="email"
id='email'
name='email'
required
autoComplete="new-email"
ref={emailInputRef}
/>
</InputGroup>
</FormGroup>
<FormGroup>
<InputGroup className="input-group-alternative">
<InputGroupAddon addonType="prepend">
<InputGroupText>
<i className="ni ni-lock-circle-open"/>
</InputGroupText>
</InputGroupAddon>
<input
placeholder="Password"
type="password"
id="password"
name="password"
required
autoComplete="new-password"
ref={passwordInputRef}
/>
</InputGroup>
</FormGroup>
<div className="custom-control custom-control-alternative custom-checkbox">
<input
className="custom-control-input"
id=" customCheckLogin"
type="checkbox"
/>
<label
className="custom-control-label"
htmlFor=" customCheckLogin"
>
<span className="text-muted">Remember me</span>
</label>
</div>
<div className="text-center">
<Button className="my-4" color="primary" type="submit">
Sign in
</Button>
</div>
</Form>
</CardBody>
</Card>
<Row className="mt-3">
<Col xs="6">
<a
className="text-light"
href="#pablo"
onClick={(e) => e.preventDefault()}
>
<small>Forgot password?</small>
</a>
</Col>
<Col className="text-right" xs="6">
<a
className="text-light"
href="#pablo"
onClick={(e) => e.preventDefault()}
>
<small>Create new account</small>
</a>
</Col>
</Row>
</Col>
</>
);
};
export default Login;
Option # 2: Using useState
import {useState} from "react";
function Login() {
const [emailState, setEmailState] = useState('');
const [passwordState, setPasswordState] = useState('');
function submitFormHandler(event) {
event.preventDefault();
console.log(`email = ${emailState} & password = ${passwordState}`);
}
const onEmailChangeHandler = (event) => {
setEmailState(event.current.value);
};
const onPasswordChangeHandler = (event) => {
setPasswordState(event.current.value);
};
return (
<>
<Col lg="5" md="7">
<Card className="bg-secondary shadow border-0">
<CardBody className="px-lg-5 py-lg-5">
<div className="text-center text-muted mb-4">
<small>Sign in</small>
</div>
<Form role="form" onSubmit={submitFormHandler}>
<FormGroup className="mb-3">
<InputGroup className="input-group-alternative">
<InputGroupAddon addonType="prepend">
<InputGroupText>
<i className="ni ni-email-83"/>
</InputGroupText>
</InputGroupAddon>
<input
placeholder="Email"
type="email"
id='email'
name='email'
required
autoComplete="new-email"
value={emailState}
onChange={onEmailChangeHandler}
/>
</InputGroup>
</FormGroup>
<FormGroup>
<InputGroup className="input-group-alternative">
<InputGroupAddon addonType="prepend">
<InputGroupText>
<i className="ni ni-lock-circle-open"/>
</InputGroupText>
</InputGroupAddon>
<input
placeholder="Password"
type="password"
id="password"
name="password"
required
autoComplete="new-password"
value={passwordState}
onChange={onPasswordChangeHandler}
/>
</InputGroup>
</FormGroup>
<div className="custom-control custom-control-alternative custom-checkbox">
<input
className="custom-control-input"
id=" customCheckLogin"
type="checkbox"
/>
<label
className="custom-control-label"
htmlFor=" customCheckLogin"
>
<span className="text-muted">Remember me</span>
</label>
</div>
<div className="text-center">
<Button className="my-4" color="primary" type="submit">
Sign in
</Button>
</div>
</Form>
</CardBody>
</Card>
<Row className="mt-3">
<Col xs="6">
<a
className="text-light"
href="#pablo"
onClick={(e) => e.preventDefault()}
>
<small>Forgot password?</small>
</a>
</Col>
<Col className="text-right" xs="6">
<a
className="text-light"
href="#pablo"
onClick={(e) => e.preventDefault()}
>
<small>Create new account</small>
</a>
</Col>
</Row>
</Col>
</>
);
};
export default Login;
Option # 3: Using both React hooks (useRef & useState).
You could use the useRef React hook for focusing purposes only and also use a local state value, using the useState React hook, for the usual purposes of login in.
Option # 4: Using custom hooks.
You can use all the mentioned before, but using primordially a custom hook, which uses the useReducer React hook under the trunk, and then useRef for focusing and useState for other purposes:
Coder example:
Custom hook:
import {useReducer} from "react";
const initialInputState = {
valueState: '',
valueIsTouchedState: false,
};
const inputStateReducer = (state, action) => {
if (action.type === 'SET_VALUE_IS_TOUCHED_STATE') {
return {
valueState: state.valueState,
valueIsTouchedState: action.payload.valueIsTouchedState
};
}
if (action.type === 'SET_VALUE_STATE') {
return {
valueState: action.payload.valueState,
valueIsTouchedState: state.valueIsTouchedState
};
}
return initialInputState;
};
const useInputReducer = (valueValidator) => {
const [inputState, dispatchFunction] = useReducer(inputStateReducer, initialInputState);
const valueIsValidState = valueValidator(inputState.valueState);
const valueInputIsInvalid = (!valueIsValidState && inputState.valueIsTouchedState);
const valueInputChangeHandler = (event) => {
dispatchFunction({type: 'SET_VALUE_IS_TOUCHED_STATE', payload: {valueIsTouchedState: true}});
dispatchFunction({type: 'SET_VALUE_STATE', payload: {valueState: event.target.value}});
};
const valueInputBlurHandler = (event) => {
dispatchFunction({type: 'SET_VALUE_IS_TOUCHED_STATE', payload: {valueIsTouchedState: true}});
// setValueState(event.target.value);
};
const setValueIsTouchedState = (value) => {
dispatchFunction({type: 'SET_VALUE_IS_TOUCHED_STATE', payload: {valueIsTouchedState: value}});
};
const resetValueInput = () => {
dispatchFunction({type: 'SET_VALUE_STATE', payload: {valueState: ''}});
dispatchFunction({type: 'SET_VALUE_IS_TOUCHED_STATE', payload: {valueIsTouchedState: false}});
};
return {
valueState: inputState.valueState,
setValueIsTouchedState,
valueIsValidState,
valueInputIsInvalid,
valueInputChangeHandler,
valueInputBlurHandler,
resetValueInput,
};
};
export default useInputReducer;
Form:
import {Fragment, useRef, useState} from 'react';
import {Prompt} from "react-router-dom";
import useInputReducer from "../../hooks/use-input-reducer";
import * as validators from "../../tools/validators";
import styles from './AuthForm.module.css';
const AuthForm = () => {
const [isLogin, setIsLogin] = useState(true);
const [startedToWork, setStartedToWork] = useState(false);
const {
valueState: emailState,
setValueIsTouchedState: setEmailIsTouchedState,
valueIsValidState: emailIsValidState, valueInputIsInvalid: emailInputIsInvalid,
valueInputChangeHandler: emailInputChangeHandler,
// valueInputBlurHandler: emailInputBlurHandler,
resetValueInput: resetEmailInput,
} = useInputReducer(validators.emailValidator);
const {
valueState: passwordState,
setValueIsTouchedState: setPasswordIsTouchedState,
valueIsValidState: passwordIsValidState, valueInputIsInvalid: passwordInputIsInvalid,
valueInputChangeHandler: passwordInputChangeHandler,
// valueInputBlurHandler: passwordInputBlurHandler,
resetValueInput: resetPasswordInput,
} = useInputReducer(validators.nameValidator);
const formIsValid = (emailIsValidState && passwordIsValidState);
const emailInputRef = useRef();
const passwordInputRef = useRef();
const submitFormHandler = (event) => {
event.preventDefault();
setEmailIsTouchedState(true);
setPasswordIsTouchedState(true);
emailInputRef.current.focus();
if (!formIsValid) {
setProperFocus();
return;
}
console.log('Submitted!. I did something!!');
const email = emailInputRef.current.value;
const password = passwordInputRef.current.value;
resetEmailInput();
resetPasswordInput();
};
const setProperFocus = () => {
if (emailInputIsInvalid) {
emailInputRef.current.focus();
} else if (passwordInputIsInvalid) {
passwordInputRef.current.focus();
}
}
const switchAuthModeHandler = () => {
setIsLogin((prevState) => !prevState);
};
const onStartedToWorkHandler = () => {
setStartedToWork(true);
};
const onFinishEnteringHandler = () => {
setStartedToWork(false);
};
const emailValidityClasses = `${styles.control}${emailInputIsInvalid ? ` ${styles.invalid}` : ''}`;
const passwordValidityClasses = `${styles.control}${passwordInputIsInvalid ? ` ${styles.invalid}` : ''}`;
return (
<Fragment>
<Prompt
when={startedToWork}
message={location =>
`Are you sure you want to go to "${location.pathname}"? \n All your entered data will be lost.`
}
/>
<section className={styles.auth}>
<h1>{isLogin ? 'Login' : 'Sign Up'}</h1>
<form
onSubmit={submitFormHandler}
onChange={onStartedToWorkHandler}
>
<div className={emailValidityClasses}>
<label htmlFor='email'>Your Email</label>
<input
type='email'
id='email'
name='email'
required
autoFocus={true}
ref={emailInputRef}
value={emailState}
onChange={emailInputChangeHandler}
// onBlur={emailInputBlurHandler}
/>
{emailInputIsInvalid ? <p className={styles['error-text']}>The Email must be valid.</p> : <p> </p>}
</div>
<div className={passwordValidityClasses}>
<label htmlFor='password'>Your Password</label>
<input
type='password'
id='password'
required
autoComplete="on"
ref={passwordInputRef}
value={passwordState}
onChange={passwordInputChangeHandler}
// onBlur={passwordInputBlurHandler}
/>
{passwordInputIsInvalid ? <p className={styles['error-text']}>The Password must not be empty.</p> : <p> </p>}
</div>
<div className={styles.actions}>
<button onClick={onFinishEnteringHandler}>{isLogin ? 'Login' : 'Create Account'}</button>
<button
type='button'
className={styles.toggle}
onClick={switchAuthModeHandler}
>
{isLogin ? 'Create new account' : 'Login with existing account'}
</button>
</div>
</form>
</section>
</Fragment>
);
};
export default AuthForm;
I never do react js but i code react native.
I think you can store the input in the state using onChangeText like this
using hooks
const index = ()=>{
const {state,setState} = useState('');
return <TextInput onChangeText={(txt)=>{
setState(txt);
console.log(state); }} />
}

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);
};

ReactJS and Material UI CheckBox State Change

Im using Material UI Checkbox to create a form that takes input and then in theory adds the new values or updates the values into a state object. This form is used for both editing a holiday or creating a new holiday.
I'm struggling with finding why my state isnt being updated upon clicking on a checkbox or typing in an input box. The checkbox wont change to checked/unchecked and the input wont remvoe the value when i use backspace to remove characters or enter new characters.
Dialog Box:
HolidayData:
{
"id": 1,
"ID": 1,
"HolidayName": "New Year's Day",
"HolidayDate": "05/20/2020",
"Branch": null,
"Hours": null,
"Web": true,
"Phone": true,
"CoOp": false,
"Active": true,
"Submitted": null,
"SubmittedBy": null,
"Published": "05/20/2020",
"PublishedBy": "John.Doe"
}
DialogBox Code:
const HolidayDialog = (props) => {
const [noticeModal, setNoticeModal] = React.useState(false);
const [selectedDate, setSelectedDate] = React.useState(new Date());
const [holidayData, setHolidayData] = React.useState(props.data);
useEffect(() => {
setHolidayData(props.data);
setNoticeModal(props.open)
});
const handleDateChange = (date) => {
setSelectedDate(date);
};
const handleClose = () => {
setNoticeModal(false);
};
const handleChange = (e) => {
const { name, checked } = e.target;
setHolidayData((prevState) => ({ ...prevState, [name]: checked }));
};
const updateValues = (e) => {
const { name, value } = e.target;
setHolidayData((prevState) => ({ ...prevState, [name]: value }));
};
return (
<Dialog
open={noticeModal}
TransitionComponent={Transition}
keepMounted
onClose={handleClose}
aria-labelledby="notice-modal-slide-title"
aria-describedby="notice-modal-slide-description"
>
<DialogTitle id="customized-dialog-title" onClose={handleClose}>
{holidayData.HolidayName ? holidayData.HolidayName : 'Create New Holiday'}
</DialogTitle>
<DialogContent dividers>
<form noValidate autoComplete="off">
<div className="row">
<div className="col">
<TextField required name="HolidayName" id="outlined-basic" label="Holiday Name" variant="outlined" onChange={updateValues} value={holidayData.HolidayName ? holidayData.HolidayName : ''}/>
</div>
<div className="col">
<TextField id="outlined-basic" label="Branch" variant="outlined" onChange={updateValues} value={holidayData.Branch ? holidayData.Branch : 'ALL'}/>
</div>
</div>
<div className="row mt-3">
<div className="col">
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<KeyboardDatePicker
disableToolbar
variant="inline"
format="MM/dd/yyyy"
margin="normal"
id="date-picker-inline"
label="Date picker inline"
value={selectedDate}
onChange={handleDateChange}
KeyboardButtonProps={{
'aria-label': 'change date',
}}
/>
</MuiPickersUtilsProvider>
</div>
<div className="col">
<TextField id="outlined-basic" label="Hours" variant="outlined" onChange={updateValues} value={holidayData.Hours ? holidayData.Hours : 'Closed'}/>
</div>
</div>
<div className="row mt-3">
<div className="col d-flex flex-column">
<FormControlLabel
control={
<Checkbox
checked={holidayData.Web ? holidayData.Web : false}
onChange={handleChange}
name="Web"
color="primary"
/>
}
label="Show on Web?"
/>
<FormControlLabel
control={
<Checkbox
checked={holidayData.CoOp ? holidayData.CoOp : false}
onChange={handleChange}
name="CoOp"
color="primary"
/>
}
label="CoOp Holiday?"
/>
</div>
<div className="col d-flex flex-column">
<FormControlLabel
control={
<Checkbox
checked={holidayData.Phone ? holidayData.Phone : false}
onChange={handleChange}
name="Phone"
color="primary"
/>
}
label="Use in IVR?"
/>
<FormControlLabel
control={
<Checkbox
checked={holidayData.Active ? holidayData.Active : false}
onChange={handleChange}
disabled
name="active"
color="primary"
/>
}
label="Active"
/>
</div>
</div>
</form>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={handleClose} color="default">
Cancel
</Button>
<Button autoFocus onClick={handleClose} color="primary">
Create Holiday
</Button>
</DialogActions>
</Dialog>
)
}
When i check a box or try to edit an input the handleChange and updateValues are firing. I believe it may be a syntax issue, but i cant seem to find anything. When console.log'ing event.target.name i get the correct name: Web for example
EDIT: The issue appears to be with the value and checked being equal to {holidayData.Phone ? holidayData.Phone : false} or the sorts. However if i bring the values down to {holidayData.Phone} itll start to throw errors:
A component is changing an uncontrolled input of type checkbox to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa).
It'll now allow me to check a checkbox but only once and throws this error over and over again and im not sure why or how to correct this?
Most probably the useEffect is causing the issue. You're resetting the data each time the component gets updated. You don't need that if you're already doing that with useState.
Check my example here with only the textbox: https://codesandbox.io/s/fancy-shape-14r63?file=/src/App.js
If you uncomment the useEffect, it stops working.

Categories