I have a form and song state where I am trying to attach a new field "appleMusicId", however whenever I do this, it resets the timeDescription and sceneDescription values.
I've been trying to fix for hours and am worried I'm overlooking something simple.
As an example when I first hit Submit I get this for the console.log values
{
"episodeId": 5,
"spotifyId": "3NanY0K4okhIQzL33U5Ad8",
"name": "Boy's a liar",
"artistName": "PinkPantheress",
"timeDescription": 12,
"sceneDescription": "First song at the party."
}
If I then click the "Add Apple ID" button, the values become this on onSubmit and the time and scene removed.
{
"episodeId": 5,
"spotifyId": "3NanY0K4okhIQzL33U5Ad8",
"name": "Boy's a liar",
"artistName": "PinkPantheress",
"appleMusicId": "dummyId"
}
Component
import { TextField } from "#/components/TextField";
import { Button } from "#chakra-ui/react";
import { Formik, Form } from "formik";
import { validate } from "graphql";
import { useState } from "react";
const initialValues = {
name: "",
artistName: "",
episodeId: undefined,
movieId: undefined,
spotifyId: "",
appleMusicId: "",
sceneDescription: "",
timeDescription: undefined,
};
const AddSong = () => {
const [song, setSong] = useState(initialValues);
const submit = (values: any) => {
console.log(values, "values");
}
const addAppleId = () => {
setSong((v) => {
return {
...v,
appleMusicId: "dummyId",
};
});
};
return (
<Formik
initialValues={song}
onSubmit={(values) => submit(values)}
validationSchema={validate}
enableReinitialize={true}
>
<Form>
<TextField name={"name"} label={"Name"} type="text" />
<TextField name={"artistName"} label={"Artist Name"} type="text" />
<TextField
name={"timeDescription"}
label={"Time Description"}
type="number"
placeholder="How many minutes in, was this song played?"
/>
<TextField
name={"sceneDescription"}
label={"Scene Description"}
type="text"
/>
<Button onClick={addAppleId}>Test Add Apple Id</Button>
<Button isLoading={isLoading} type={"submit"}>
Create
</Button>
</Form>
</Formik>
)
};
TextField (if you need to see it)
export const TextField = ({ label, value, ...props }: TextFieldProps) => {
const [field, meta] = useField(props);
return (
<FormControl
isInvalid={
meta.touched && (meta.error && meta.error?.length > 0 ? true : false)
}
mb={3}
>
<FormLabel
aria-label={field.name}
htmlFor={field.name}
mb={0}
textColor={useColorModeValue('gray.700', 'gray.100')}
>
{label}
</FormLabel>
<Input
{...field}
{...props}
value={value ? value : undefined}
autoComplete="off"
borderRadius={0}
paddingLeft={2}
border={"2px solid"}
borderColor={"gray.200"}
_invalid={{ borderColor: "error-red" }}
/>
<ErrorMessage
name={field.name}
component="span"
className="text-sm pt-2 text-red-error"
/>
<Text pt={1} fontSize={'sm'} color={useColorModeValue('gray.400', 'gray.600')}>{props.footerText}</Text>
</FormControl>
);
};
Hi Bro I have send you proposal on UW Plathform . Please accept it will reolve issue
Also It would be grat if you check out my proposal.
Best
Asadbek Savronov!
It seems like the issue is happening because you are updating the song state outside of the Formik component and therefore the form values are not updating.
To fix the issue, you need to update the form values within the Formik component. You can do this by passing the setFieldValue prop to your "Add Apple ID" button and updating the form values directly within the Formik component.
Possible Solution:
const AddSong = () => {
return (
<Formik
initialValues={initialValues}
onSubmit={(values) => console.log(values)}
validationSchema={validate}
enableReinitialize={true}
>
{({ setFieldValue }) => (
<Form>
<TextField name={"name"} label={"Name"} type="text" />
<TextField name={"artistName"} label={"Artist Name"} type="text" />
<TextField
name={"timeDescription"}
label={"Time Description"}
type="number"
placeholder="How many minutes in, was this song played?"
/>
<TextField
name={"sceneDescription"}
label={"Scene Description"}
type="text"
/>
<Button onClick={() => setFieldValue("appleMusicId", "dummyId")}>
Test Add Apple Id
</Button>
<Button isLoading={isLoading} type={"submit"}>
Create
</Button>
</Form>
)}
</Formik>
);
};
no issue in your code.
I have tried to create my project and run it.
import { Formik, Form, useField, ErrorMessage, } from "formik";
// import { validate } from "graphql";
import { useState } from "react";
import {FormControl, FormLabel, Input} from '#material-ui/core'
import {useColorModeValue, Button, Text} from '#chakra-ui/react'
const TextField = ({ label, value, ...props }) => {
const [field, meta] = useField(props);
return (
<FormControl
isInvalid={
meta.touched && (meta.error && meta.error?.length > 0 ? true : false)
}
mb={3}
>
<FormLabel
aria-label={field.name}
htmlFor={field.name}
mb={0}
textColor={useColorModeValue('gray.700', 'gray.100')}
>
{label}
</FormLabel>
<Input
{...field}
{...props}
value={value ? value : undefined}
autoComplete="off"
borderRadius={0}
paddingLeft={2}
border={"2px solid"}
borderColor={"gray.200"}
_invalid={{ borderColor: "error-red" }}
/>
<ErrorMessage
name={field.name}
component="span"
className="text-sm pt-2 text-red-error"
/>
<Text pt={1} fontSize={'sm'} color={useColorModeValue('gray.400', 'gray.600')}>{props.footerText}</Text>
</FormControl>
);
};
const initialValues = {
name: "111111111",
artistName: "11111111",
appleMusicId: "",
sceneDescription: "12",
timeDescription: 1,
};
export const AddSong = () => {
const [song, setSong] = useState(initialValues);
const [isLoading, setIsLoading] = useState(true)
const submit = (values) => {
console.log(values, "values");
}
const addAppleId = () => {
setSong((v) => {
return {
...v,
appleMusicId: "dummyId",
};
});
};
return (
<Formik
initialValues={song}
onSubmit={(values) => submit(values)}
// validationSchema={validate}
enableReinitialize={true}
>
<Form>
<TextField name={"name"} label={"Name"} type="text" />
<TextField name={"artistName"} label={"Artist Name"} type="text" />
<TextField
name={"timeDescription"}
label={"Time Description"}
type="number"
placeholder="How many minutes in, was this song played?"
/>
<TextField
name={"sceneDescription"}
label={"Scene Description"}
type="text"
/>
<Button onClick={addAppleId}>Test Add Apple Id</Button>
<Button type={"submit"}>
Create
</Button>
</Form>
</Formik>
)
};
I am getting log like this:
{name: '111111111', artistName: '11111111', appleMusicId: '', sceneDescription: '12', timeDescription: 1}
and after adding dummy apple id:
{name: '111111111', artistName: '11111111', appleMusicId: 'dummyId', sceneDescription: '12', timeDescription: 1}
Related
I am new to react and I have just started using Formik
I like how simple it makes making forms and handling forms in react.
I have created multiple custom fields using formik, I am putting the react-select field I created as an example here.
import { ErrorMessage, Field } from "formik";
import React from "react";
import Select from 'react-select'
const SelectInput = (props) => {
const { label, name, id,options, required, ...rest } = props;
const defaultOptions = [
{label : `Select ${label}`,value : ''}
]
const selectedOptions = options ? [...defaultOptions,...options] : defaultOptions
return (
<div className="mt-3">
<label htmlFor={id ? id : name}>
{label} {required && <span className="text-rose-500">*</span>}
</label>
<Field
// className="w-full"
name={name}
id={id ? id : name}
>
{(props) => {
return (
<Select
options={selectedOptions}
onChange={(val) => {
props.form.setFieldValue(name, val ? val.value : null);
}}
onClick = {(e)=>{e.stopPropagation}}
{...rest}
// I want someting like onReset here
></Select>
);
}}
</Field>
<ErrorMessage
name={name}
component="div"
className="text-xs mt-1 text-rose-500"
/>
</div>
);
};
export default SelectInput;
This is the usual code I use for submitting form as you can see I am using resetForm() method that is provided by formik, I want to attach the reseting logic in on submit method itself.
const onSubmit = async (values, onSubmitProps) => {
try {
//send request to api
onSubmitProps.resetForm()
} catch (error) {
console.log(error.response.data);
}
};
If you want to reset the selected value after the form is submitted, you need to provide a controlled value for the Select component.
The Formik Field component provides the value in the props object, so you can use it.
For example:
SelectInput.js
import { ErrorMessage, Field } from 'formik';
import React from 'react';
import Select from 'react-select';
const SelectInput = ({ label, name, id, options, required, ...rest }) => {
const defaultOptions = [{ label: `Select ${label}`, value: '' }];
const selectedOptions = options ? [...defaultOptions, ...options] : defaultOptions;
return (
<div className='mt-3'>
<label htmlFor={id ? id : name}>
{label} {required && <span className='text-rose-500'>*</span>}
</label>
<Field
// className="w-full"
name={name}
id={id ? id : name}
>
{({
field: { value },
form: { setFieldValue },
}) => {
return (
<Select
{...rest}
options={selectedOptions}
onChange={(val) => setFieldValue(name, val ? val : null)}
onClick={(e) => e.stopPropagation()}
value={value}
/>
);
}}
</Field>
<ErrorMessage name={name} component='div' className='text-xs mt-1 text-rose-500' />
</div>
);
};
export default SelectInput;
and Form.js
import { Formik, Form } from 'formik';
import SelectInput from './SelectInput';
function App() {
return (
<Formik
initialValues={{
firstName: '',
}}
onSubmit={async (values, { resetForm }) => {
console.log({ values });
resetForm();
}}
>
<Form>
<SelectInput
name='firstName'
label='First Name'
options={[{ label: 'Sam', value: 'Sam' }]}
/>
<button type='submit'>Submit</button>
</Form>
</Formik>
);
}
export default App;
Therefore, if you click the Submit button, value in the Select component will be reset.
You can also make a useRef hook to the Fromik component and then reset the form within the reset function without adding it as a parameter to the function.
https://www.w3schools.com/react/react_useref.asp
It's one of the really nice hooks you'll learn as you progress through React :)
So if I understood you correctly you want to reset a specif field value onSubmit rather than resetting the whole form, that's exactly what you can achieve using actions.resetForm().
Note: If nextState is specified, Formik will set nextState.values as the new "initial state" and use the related values of nextState to update the form's initialValues as well as initialTouched, initialStatus, initialErrors. This is useful for altering the initial state (i.e. "base") of the form after changes have been made.
You can check this in more detail here.
And here is an example of resetting a specific field using resetForm() whereby you can see as you input name, email and upon submit only email field will get empty using resetForm.
import "./styles.css";
import React from "react";
import { Formik } from "formik";
const initialState = {
name: "",
email: ""
};
const App = () => (
<div>
<h1>My Form</h1>
<Formik
initialValues={initialState}
onSubmit={(values, actions) => {
console.log(values, "values");
actions.resetForm({
values: {
email: initialState.email
}
});
}}
>
{(props) => (
<form onSubmit={props.handleSubmit}>
<input
type="text"
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.name}
name="name"
/>
<br />
<input
type="text"
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.email}
name="email"
/>
<br />
<br />
{props.errors.name && <div id="feedback">{props.errors.name}</div>}
<button type="submit">Submit</button>
</form>
)}
</Formik>
</div>
);
export default App;
Is it possible to substitute the value from useState with the one coming from input?
Or is there a way to do this using dispatch?
I have tried many ways, but none of them work.
const renderInput = ({
input,
label,
type,
meta: { asyncValidating, touched, error },
}) => {
const [value, setValue] = useState('default state');
const onChange = event => {
setValue(event.target.value);
// + some logic here
};
return (
<div>
<label>{label}</label>
<div className={asyncValidating ? 'async-validating' : ''}>
<input {...input} value={value} onChange={onChange} type={type} placeholder={label} />
{touched && error && <span>{error}</span>}
</div>
</div>
);
};
const SelectingFormValuesForm = props => {
const { type, handleSubmit, pristine, reset, submitting } = props;
return (
<form onSubmit={handleSubmit}>
<div>
<label>Name</label>
<div>
<Field
name="name"
component={renderInput}
type="text"
placeholder="Dish name..."
/>
</div>
</div>
</form>
);
};
SelectingFormValuesForm = reduxForm({
form: 'selectingFormValues',
validate,
asyncValidate,
// asyncBlurFields: ['name'],
})(SelectingFormValuesForm);
export default SelectingFormValuesForm;
This way, unfortunately, the value sent to the submit remains empty.
I am trying to create a custom component that iterates over an array of data and display for each item an input field type radio with its according label, it works but for some reason the data is displayed three times, instead of just once for each field and I cant figure out why, I just want to render just three inputs, why does this behavior occur, am I missing something? Here is my code:
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Tooltip } from '#progress/kendo-react-tooltip';
import { Form, Field, FormElement } from '#progress/kendo-react-form';
import { RadioGroup, RadioButton } from '#progress/kendo-react-inputs';
import { Error } from '#progress/kendo-react-labels';
import { Input } from '#progress/kendo-react-inputs';
const emailRegex = new RegExp(/\S+#\S+\.\S+/);
const data = [
{
label: 'Female',
value: 'female',
},
{
label: 'Male',
value: 'male',
},
{
label: 'Other',
value: 'other',
},
];
const emailValidator = (value) =>
emailRegex.test(value) ? '' : 'Please enter a valid email.';
const EmailInput = (fieldRenderProps) => {
const { validationMessage, visited, ...others } = fieldRenderProps;
return (
<div>
<Input {...others} />
{visited && validationMessage && <Error>{validationMessage}</Error>}
</div>
);
};
const Component = () => {
return (
<>
{data.map((item, index) => {
return (
<p>
<input
name="group1"
type="radio"
value={item.value}
label={item.label}
key={index}
/>
<label>{item.label}</label>
</p>
);
})}
</>
);
};
const App = () => {
const handleSubmit = (dataItem) => alert(JSON.stringify(dataItem, null, 2));
const tooltip = React.useRef(null);
return (
<Form
onSubmit={handleSubmit}
render={(formRenderProps) => (
<FormElement
style={{
maxWidth: 650,
}}
>
<fieldset className={'k-form-fieldset'}>
<legend className={'k-form-legend'}>
Please fill in the fields:
</legend>
<div className="mb-3">
<Field
name={'firstName'}
component={Input}
label={'First name'}
/>
</div>
<div className="mb-3">
<Field name={'lastName'} component={Input} label={'Last name'} />
</div>
<div className="mb-3">
<Field
name={'email'}
type={'email'}
component={EmailInput}
label={'Email'}
validator={emailValidator}
/>
</div>
</fieldset>
<div
onMouseOver={(event) =>
tooltip.current && tooltip.current.handleMouseOver(event)
}
onMouseOut={(event) =>
tooltip.current && tooltip.current.handleMouseOut(event)
}
>
<RadioGroup data={data} item={Component} />
<Tooltip
ref={tooltip}
anchorElement="target"
position="right"
openDelay={300}
parentTitle={true}
/>
</div>
<div className="k-form-buttons">
<button
type={'submit'}
className="k-button k-button-md k-rounded-md k-button-solid k-button-solid-base"
disabled={!formRenderProps.allowSubmit}
>
Submit
</button>
</div>
</FormElement>
)}
/>
);
};
ReactDOM.render(<App />, document.querySelector('my-app'));
and here is an example:
https://stackblitz.com/edit/react-rfb1ux-jpqvwy?file=app/main.jsx
<RadioGroup data={[1]} item={Component} />
RadioGroup assumes that Component element is to be rendered over data array ... so it renders like this -->
data.map(val=><Component />)
here size of data array is 3 .
or
<Component />
Radio Group will do the mapping for you. Try changing Component to:
const Component = (props) => {
return (
<p>
<input
name="group1"
type="radio"
value={props.children.props.value}
label={props.children.props.label}
key={props.children.props.label}
/>
<label>{props.children.props.label}</label>
</p>
);
};
This will then map out the items for you.
seems like this works,
const Component = ({ children: { props } }) => {
const { value, label } = { ...props };
return (
<p>
<input name="group1" type="radio" value={value} label={label} />
<label>{label}</label>
</p>
);
};
What are the types of React.js's state and event?
In my code below, I can only make it work by using type: any but it's just a hack. How can I use the right types for them?
In my custom hooks:
If I use function useFormFields(initialState: object), I get:
// value={inputs.item} error:
Property 'item' does not exist on type 'object'.ts(2339)
// onChange function error:
(JSX attribute) onChange: object
No overload matches this call.
If I use function(event: React.FormEvent) (which is true), I have this error:
Property 'id' does not exist on type 'EventTarget'.ts(2339)
If I use function(event: object), I have this error:
Property 'target' does not exist on type 'object'.ts(2339)
That's odd because below I use const handleSubmitItem = (event: React.FormEvent) and it works.
The answers I've found (like this one) don't work for me because Property 'id' does not exist on type 'EventTarget'
import React, {useState} from 'react';
import TextField from '#material-ui/core/TextField';
import IconButton from '#material-ui/core/IconButton';
import AddShoppingCartIcon from '#material-ui/icons/AddShoppingCart';
/**
* Custom hooks for input fields.
* #param initialState initialState for Input Fields
*/
function useFormFields(initialState: any) {
const [inputs, setValues] = useState(initialState);
return [
inputs,
function(event: any) {
setValues({
...inputs,
[event.target.id]: event.target.value
});
}
];
}
export default function FormPropsTextFields() {
const [inputs, handleInputChange] = useFormFields({
item: '',
quantity: '',
store: ''
});
const handleSubmitItem = (event: React.FormEvent) => {
event.preventDefault();
console.log(inputs);
};
return (
<form
className={classes.root}
noValidate autoComplete="off"
onSubmit={handleSubmitItem}
>
<div>
<TextField
required id="item"
label="Item"
value={inputs.item}
onChange={handleInputChange}
/>
<TextField
id="quantity"
label="Quantity"
type="number"
value={inputs.quantity}
onChange={handleInputChange}
InputLabelProps={{
shrink: true,
}}
/>
<TextField
id="store"
label="Store"
type="search"
value={inputs.store}
onChange={handleInputChange}
/>
<IconButton
type="submit"
color="primary"
aria-label="add to shopping cart"
>
<AddShoppingCartIcon />
</IconButton>
</div>
</form>
);
}
I've made some corrections in the solution that you found.
Hope it helps!
import React, {useState} from 'react';
import TextField from '#material-ui/core/TextField';
import IconButton from '#material-ui/core/IconButton';
import AddShoppingCartIcon from '#material-ui/icons/AddShoppingCart';
/**
* Custom hooks for input fields.
* #param initialState initialState for Input Fields
*/
export interface MyModel {
item: string
quantity: string
store: string
}
function useFormFields<T>(initialState: T): [T, (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => void] {
const [inputs, setValues] = useState<T>(initialState);
return [
inputs,
function (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) {
setValues({
...inputs,
[event.target.id]: event.target.value
});
}
];
}
export default function FormPropsTextFields() {
const [inputs, handleInputChange] = useFormFields<MyModel>({
item: '',
quantity: '',
store: ''
});
const handleSubmitItem = (event: React.FormEvent) => {
event.preventDefault();
console.log(inputs);
};
return (
<form
className={classes.root}
noValidate autoComplete="off"
onSubmit={handleSubmitItem}
>
<div>
<TextField
required id="item"
label="Item"
value={inputs.item}
onChange={handleInputChange}
/>
<TextField
id="quantity"
label="Quantity"
type="number"
value={inputs.quantity}
onChange={handleInputChange}
InputLabelProps={{
shrink: true,
}}
/>
<TextField
id="store"
label="Store"
type="search"
value={inputs.store}
onChange={handleInputChange}
/>
<IconButton
type="submit"
color="primary"
aria-label="add to shopping cart"
>
<AddShoppingCartIcon />
</IconButton>
</div>
</form>
);
}
Since every component may be different you need to define the state and props types by yourself. There are some basic types defined for react (because every component may have children for example) but as i said, you will need to define your own types.
An example for a functional component:
const App: React.FC<{ message: string }> = ({ message }) => (
<div>{message}</div>
);
The example above could also be written like so:
type MyType = { message: string }
const App: React.FC<MyType> = ({ message }) => (
<div>{message}</div>
);
further reading here:
https://github.com/typescript-cheatsheets/react-typescript-cheatsheet#section-2-getting-started
Generals! the answer!
hope it helps!
import IconButton from '#material-ui/core/IconButton';
import TextField from '#material-ui/core/TextField';
import AddShoppingCartIcon from '#material-ui/icons/AddShoppingCart';
import React, { useState } from 'react';
export interface MyModel {
item: string;
quantity: string;
store: string;
}
/**
* Custom hooks for input fields.
* #param initialState initialState for Input Fields
**/
function useFormFields<T>(initialState: T) {
const [inputs, setValues] = useState(initialState);
return [
inputs,
function(event: React.FormEvent) {
const {name, value} = event.currentTarget;
setValues({
...inputs,
[name]: value
});
}
];
}
export default function FormPropsTextFields() {
const [inputs, handleInputChange] = useFormFields<MyModel>({
item: '',
quantity: '',
store: '',
});
const handleSubmitItem = (event: React.MouseEvent<HTMLButtonElement>) => {
event.preventDefault();
/***
make sure to have whatever attribute you want in the "html tag"
*/
const { name, value } = event.currentTarget;
console.log(inputs);
};
return (
<form
className={classes.root}
noValidate autoComplete="off"
>
<div>
<TextField
required id="item"
label="Item"
name="Item"
value={inputs.item}
onChange={handleInputChange}
/>
<TextField
id="quantity"
label="Quantity"
name="Quantity"
type="number"
value={inputs.quantity}
onChange={handleInputChange}
InputLabelProps={{
shrink: true,
}}
/>
<TextField
id="store"
label="Store"
name="Store"
type="search"
value={inputs.store}
onChange={handleInputChange}
/>
<IconButton
type="button"
color="primary"
aria-label="add to shopping cart"
onClick={handleSubmitItem}
>
<AddShoppingCartIcon />
</IconButton>
</div>
</form>
);
}
I am making a form like
I want the add button to be active whenever user is changing the "Tags" input text.
I am using material-ui and I made a Input component.
const SingleInput = (props) => (
<Fragment>
<FormControl margin="normal" required fullWidth>
<TextField
id={props.id}
type={props.type}
name={props.name}
label={props.label}
value={props.content}
variant={props.variant}
placeholder ={props.placeholder}
onChange={props.controlFunc}>
</TextField>
</FormControl>
</Fragment>
);
export default SingleInput;
and I import this into my form and like:
class AddCompanyForm extends React.Component {
constructor() {
super();
this.state = {
company_name: "",
company_description: "",
company_tag: "",
company_tags: "",
company_contact: "",
disabled: true
};
this.handleOnSubmit = this.handleOnSubmit.bind(this);
this.handleOnChange = this.handleOnChange.bind(this);
this.handleOnSelect = this.handleOnSelect.bind(this);
}
handleOnChange(e) {
e.preventDefault();
this.setState({ [e.target.name]: e.target.value });
this.setState({ disabled: false });
console.log("teg", this.state.company_tag.length);
console.log("cont", this.state.company_contact.length);
if (this.state.company_tag.length == 1) {
this.setState({ disabled: true });
}
}
handleOnSubmit(e) {
e.preventDefault();
this.props.createCompany(this.state);
}
handleOnSelect(e) {
e.preventDefault();
chipsValue.push(this.state.company_tag);
this.setState({
company_tags: chipsValue,
company_tag: "",
disabled: true
});
}
render() {
return (
<Paper style={styles.paper}>
<Avatar>
<LockIcon />
</Avatar>
<Typography variant="headline">Add a New Company</Typography>
<form onSubmit={this.handleOnSubmit}>
<SingleInput
id={"company_name"}
type={"company_name"}
name={"company_name"}
label={"Company Name"}
content={this.state.company_name}
controlFunc={this.handleOnChange}
variant={"filled"}
/>
<SingleInput
id={"company_description"}
type={"company_description"}
name={"company_description"}
label={"Description"}
content={this.state.company_description}
controlFunc={this.handleOnChange}
variant={"filled"}
/>
<SingleInput
id={"company_tags"}
type={"company_tags"}
name={"company_tag"}
label={"Tags (to add dropdown here!)"}
content={this.state.company_tag}
controlFunc={this.handleOnChange}
variant={"filled"}
/>
<Button
disabled={this.state.disabled}
onClick={this.handleOnSelect}
variant="raised"
color="secondary"
>
Add
</Button>
<SingleInput
id={"company_contact"}
type={"company_contact"}
name={"company_contact"}
label={"Contact"}
content={this.state.company_contact}
controlFunc={this.handleOnChange}
variant={"filled"}
/>
<Button type="submit" fullWidth variant="raised" color="primary">
Add Company
</Button>
</form>
</Paper>
);
}
}
const mapDispatchToProps = dispatch =>
bindActionCreators(
{
createCompany
},
dispatch
);
export default connect(
null,
mapDispatchToProps
)(AddCompanyForm);
Now the problem is even when I change "Company Name" or any other input button, the Add button becomes enabled.
Any help is very much appreciated.
Check the example below using React Hooks for the button state, and the onChange property of the TextField to set it.
export default function TextFieldAndButton (props) {
const [btnDisabled, setBtnDisabled] = useState(true)
return (
<>
<TextField
onChange={(text) => setBtnDisabled(!text.target.value)}
/>
<Button disabled={btnDisabled}>OK</Button>
</>
)
}
The problem here is that setState is async and you are using state values inside handleOnChange before it is updated. Either use setState callback to calculate disable or a better way is to calc disabled in render. This approach makes it much simpler and even works while rendering for the first time.
render() {
const disabled = !this.state.company_tag.length;
return (
// ...
<Button
disabled={disabled}
onClick={this.handleOnSelect}
variant="raised"
color="secondary"
>
Add
</Button>
// ...
);
}