How to bind multiple onChange callback for different inputs? - javascript

I am making a different form component using ant design and trying to bind several different inputs
Here is the code :
this.state = {
title: '',
product: '',
options: 0,
price: 0,
}
onTitleChange = (e) => {
this.setState({
title: e.target.value
})
}
onProductChange = (e) => {
this.setState({
product: e.target.value
})
}
onHandleChange = value => {
this.setState({
priceOption: value
})
}
onNumberChange = e => {
this.setState({
price: e.target.value
})
}
<FormItemRow>
<Col span={24} style={colStyle}>
<FormItem label={'title'} colon={false} style={{ marginBottom: 0 }}>
{getFieldDecorator('title', {
rules: [
{ required: true, message: 'title is required' },
],
})(<Input onChange={this.onTitleChange}/>)}
</FormItem>
</Col>
</FormItemRow>
<FormItemRow>
<Col span={24} style={colStyle}>
<FormItem label={'product-number'} colon={false} style={{ marginBottom: 0 }}>
{getFieldDecorator('product-number', {
rules: [{ required: true, message: 'product-number is required' }],
})(<Input onChange={this.onProductChange}/>)}
</FormItem>
</Col>
</FormItemRow>
<FormItemRow>
<Col span={12} style={colStyle}>
<FormItem label={'options'} colon={false} style={{ marginBottom: 0 }}>
{getFieldDecorator('options', {
rules: [
{ required: true, message: 'options is required' },
],
})(<Select onChange={this.onHandleChange}>{this.handWashOptions(this.props.handWashOptions)}</Select>)}
</FormItem>
</Col>
<Col span={12} style={colStyle}>
<FormItem label={'price'} colon={false} style={{ marginBottom: 0 }}>
{getFieldDecorator('price', {
rules: [
{ required: true, message: 'price is required' },
],
})(<Input type="number" onChange={this.onNumberChange}/>)}
</FormItem>
</Col>
</FormItemRow>
title and product using just Input component.
option is using Select component.
and price is using Input type number component.
I think it is very inefficient using different onChange callback on each input component.
Is there any way I can bind one onChange callback function?

You can simply make a generic function handleChange, pass the name and value which are to be updated
handleChange(name,value){
this.setState({[name]: value})
}
and pass value to handle change like this
<Input onChange={(name,value)=>this.onTitleChange(name,value)}/>
You can use e.target.value to get value from target, also if you need to have different logic for some particular element then you can simply add a exception in handleChange

this.state = {
fields:{
each:
field:
you:
have:
}
}
handleChange = (event) => {
const { value, type, name } = event.currentTarget
console.log(type, value, name);
this.setState(prevState => ({
fields: {
...prevState.fields,
[name]: type === 'number' ? parseInt(value, 10) : value
}
}));
};

As stated in Handling Multiple Inputs,
add a name attribute to each element and let the handler function choose what to do based on the value of event.target.name.
So you can have:
onChange = event => {
const name = event.target.name;
this.setState({
[name]: event.target.value
})
}
// Add `name` attribute used for event.target.name
<Select onChange={this.onChange} name="wash">...</Select>
<Input type="number" onChange={this.onChange} name="number"/>
<Input onChange={this.onChange} name="product" />

Related

implement loading screen react

So i have this react / node app where basically when I hit register after entering in all my details, it takes me to the stripe checkout page. However, i've been reading firebase goes into a cold start, after 2-3 minutes of no invocations. This is a problem, because we don't expect to get large traffic into our app for another 6 months, which means that there isn't going to be a person using the app every 2-3 minutes.
Right now, during that limbo period between registration and stripe checkout, it goes into a white screen, for like 8 seconds. How can I get this to be a loading screen instead, like one of those react spinning loaders as a temp fix so our customers don't get worried the app is breaking on them?
here's my code for the registration page and then the function that calls stripe:
let firestore = firebase.firestore()
const FormItem = Form.Item
var userIDStripeSubmit = ""
class RegistrationForm extends React.Component {
state = {
confirmDirty: false,
}
onSubmit = (event) => {
event.preventDefault();
this.props.form.validateFields((err, values) => {
if (err) return
const email = this.props.form.getFieldValue('email')
const passwordOne = this.props.form.getFieldValue('password1')
const firstName = this.props.form.getFieldValue('First Name')
const lastName = this.props.form.getFieldValue('Last Name')
const companyName = this.props.form.getFieldValue('Company Name')
const {
history,
} = this.props
AuthorizationHome.doCreateUserWithEmailAndPassword(email, passwordOne)
.then((authUser) => facades.userFacade().doCreateUser(authUser.user.uid, email))
.catch(error => {
this.setState({'error': error})
})
// adding into profiledata collection
// var userID = ""
firebase.auth().onAuthStateChanged((user) => {
if(user) {
console.log(user.uid)
userIDStripeSubmit = user.uid
console.log("userid inside firebase auth is" + user.uid)
// var firstNameFromField =
firestore.collection('profiledata').doc(user.uid).set({
firstname: firstName,
lastname: lastName,
companyname: companyName,
accountStatus: "inactive",
})
.catch(error => {
alert(error.message);
});
createCheckoutSession(user.uid)
}
})
// firestore collection query
})
}
handleConfirmBlur = (e) => {
const value = e.target.value;
this.setState({ confirmDirty: this.state.confirmDirty || !!value });
}
compareToFirstPassword = (rule, value, callback) => {
const form = this.props.form;
if (value && value !== form.getFieldValue('password1')) {
callback('Passwords do not match!');
} else {
callback();
}
}
validateToNextPassword = (rule, value, callback) => {
const form = this.props.form;
if (value && this.state.confirmDirty) {
form.validateFields(['password2'], { force: true });
}
callback();
}
render() {
const { getFieldDecorator } = this.props.form
const { error } = this.state
return (
<Form onSubmit={this.onSubmit} hideRequiredMark={true} className="registration-form" style={{ marginBottom: "0px" }}>
{ error && <Alert type="error" message={error.message}/> }
<FormItem label="Email" colon={false} style={{ marginBottom: "0px" }}>
{getFieldDecorator('email', {
rules: [{
type: 'email', message: 'Invalid email address',
}, {
required: true, message: 'Please input your email address',
}],
})(
<Input placeholder="Enter email" />
)}
</FormItem>
{/* */}
<FormItem label="First Name" colon={false} style={{ marginBottom: "0px" }}>
{getFieldDecorator('First Name', {
rules: [{
required: true, message: 'Please enter your First Name',
}],
})(
<Input type="text" placeholder="Enter Your First Name"/>
)}
</FormItem>
<FormItem label="Last Name" colon={false} style={{ marginBottom: "0px" }}>
{getFieldDecorator('Last Name', {
rules: [{
required: true, message: 'Please enter your Last Name',
}],
})(
<Input type="text" placeholder="Enter Your Last Name"/>
)}
</FormItem>
<FormItem label="Company Name" colon={false} style={{ marginBottom: "0px" }}>
{getFieldDecorator('Company Name', {
rules: [{
required: true, message: 'Please enter your Company Name',
}],
})(
<Input type="text" placeholder="Enter Your Company Name"/>
)}
</FormItem>
<FormItem label="Password" colon={false} style={{ marginBottom: "0px" }}>
{getFieldDecorator('password1', {
rules: [{
required: true, message: 'Please choose a password',
}, {
validator: this.validateToNextPassword,
}],
})(
<Input type="password" placeholder="Enter password"/>
)}
</FormItem>
<FormItem label="Confirm Password" colon={false} style={{ marginBottom: "0px" }}>
{getFieldDecorator('password2', {
rules: [{
required: true, message: 'Please confirm your password',
}, {
validator: this.compareToFirstPassword,
}],
})(
<Input type="password" onBlur={this.handleConfirmBlur} placeholder="Confirm password" />
)}
</FormItem>
<FormItem>
<Button type="primary" htmlType="submit" id="submitButton">Register</Button>
</FormItem>
</Form>
);
}
}
const WrappedRegistrationForm = Form.create()(RegistrationForm);
export default withRouter(WrappedRegistrationForm)
i am new to react, so please let me know if there's anything I can do to make the code easier to read or anything. I'm not sure how to create or where to put the loading spinner
You will need to implement a loading default that is displayed in your base HTML file, this is found inside your public directory as index.html.
Please refer to this answer: https://stackoverflow.com/a/60404990/2301161

page not displaying elements react

this may not be able to be answered, but I am going to try to include as much detail as possible. I have this page called registrationForm.js, which in my routes.js is set to render with the path /signup. It does render the page, but it is blank. When I refresh the page, for a split second I see all the elements I'm supposed to, the input fields, the buttons, the images. But for some reason, it goes back to being fully white page.
Here is the entire registrationForm.js code!
import React from 'react'
import './RegistrationForm.css'
import { Alert, Button, Form, Input } from 'antd'
import { AuthorizationHome } from '../models'
import { withRouter } from 'react-router-dom'
import * as routes from '../routes'
import facades from '../facades'
import firebase from 'firebase'
import { useState } from "react";
import {createCheckoutSession} from '../Stripepayment/checkout'
let firestore = firebase.firestore()
const FormItem = Form.Item
// const [firstname, setFirstName] = useState("");
// const [lastname, setLastName] = useState("");
// const [companyname, setCompanyName] = useState("");
// const [accountStatus, setAccountStatus] = useState("");
var userIDStripeSubmit = ""
class RegistrationForm extends React.Component {
state = {
confirmDirty: false,
}
onSubmit = (event) => {
event.preventDefault();
this.props.form.validateFields((err, values) => {
if (err) return
const email = this.props.form.getFieldValue('email')
const passwordOne = this.props.form.getFieldValue('password1')
const firstName = this.props.form.getFieldValue('First Name')
const lastName = this.props.form.getFieldValue('Last Name')
const companyName = this.props.form.getFieldValue('Company Name')
const {
history,
} = this.props
AuthorizationHome.doCreateUserWithEmailAndPassword(email, passwordOne)
.then((authUser) => facades.userFacade().doCreateUser(authUser.user.uid, email))
// .then((authUser) => {
// history.push(createCheckoutSession(authUser.user.uid))
// })
.catch(error => {
this.setState({'error': error})
})
// adding into profiledata collection
// var userID = ""
firebase.auth().onAuthStateChanged((user) => {
if(user) {
// console.log("profile.js " + user.uid)
// userID = user.uid
// userID = user.uid
console.log(user.uid)
userIDStripeSubmit = user.uid
console.log("userid inside firebase auth is" + user.uid)
// var firstNameFromField =
// console.log("this props : " + this.props.form.getFieldValue('First Name'))
firestore.collection('profiledata').doc(user.uid).set({
firstname: firstName,
lastname: lastName,
companyname: companyName,
accountStatus: "inactive",
})
.catch(error => {
alert(error.message);
});
createCheckoutSession(user.uid)
}
})
// firestore collection query
// setFirstName("");
// setLastName("");
// setCompanyName("");
// setAccountStatus("")
})
}
handleConfirmBlur = (e) => {
const value = e.target.value;
this.setState({ confirmDirty: this.state.confirmDirty || !!value });
}
compareToFirstPassword = (rule, value, callback) => {
const form = this.props.form;
if (value && value !== form.getFieldValue('password1')) {
callback('Passwords do not match!');
} else {
callback();
}
}
validateToNextPassword = (rule, value, callback) => {
const form = this.props.form;
if (value && this.state.confirmDirty) {
form.validateFields(['password2'], { force: true });
}
callback();
}
render() {
const { getFieldDecorator } = this.props.form
const { error } = this.state
return (
<Form onSubmit={this.onSubmit} hideRequiredMark={true} className="registration-form" style={{ marginBottom: "0px" }}>
{ error && <Alert type="error" message={error.message}/> }
<FormItem label="Email" colon={false} style={{ marginBottom: "0px" }}>
{getFieldDecorator('email', {
rules: [{
type: 'email', message: 'Invalid email address',
}, {
required: true, message: 'Please input your email address',
}],
})(
<Input placeholder="Enter email" />
)}
</FormItem>
{/* */}
<FormItem label="First Name" colon={false} style={{ marginBottom: "0px" }}>
{getFieldDecorator('First Name', {
rules: [{
required: true, message: 'Please enter your First Name',
}],
})(
<Input type="text" placeholder="Enter Your First Name"/>
)}
</FormItem>
<FormItem label="Last Name" colon={false} style={{ marginBottom: "0px" }}>
{getFieldDecorator('Last Name', {
rules: [{
required: true, message: 'Please enter your Last Name',
}],
})(
<Input type="text" placeholder="Enter Your Last Name"/>
)}
</FormItem>
<FormItem label="Company Name" colon={false} style={{ marginBottom: "0px" }}>
{getFieldDecorator('Company Name', {
rules: [{
required: true, message: 'Please enter your Company Name',
}],
})(
<Input type="text" placeholder="Enter Your Company Name"/>
)}
</FormItem>
<FormItem label="Password" colon={false} style={{ marginBottom: "0px" }}>
{getFieldDecorator('password1', {
rules: [{
required: true, message: 'Please choose a password',
}, {
validator: this.validateToNextPassword,
}],
})(
<Input type="password" placeholder="Enter password"/>
)}
</FormItem>
<FormItem label="Confirm Password" colon={false} style={{ marginBottom: "0px" }}>
{getFieldDecorator('password2', {
rules: [{
required: true, message: 'Please confirm your password',
}, {
validator: this.compareToFirstPassword,
}],
})(
<Input type="password" onBlur={this.handleConfirmBlur} placeholder="Confirm password" />
)}
</FormItem>
{/* inserting into profiledata collection firestore */}
{/* ending profiledata collection insert firestore */}
{/*
<FormItem label="First Name" colon={false} >
<Input placeholder = "Enter Your First Name" style={{ maxWidth: '100%', maxHeight: '50%' , }}
value = { firstname }
onChange = {
(e) => setFirstName(e.target.value)
}/>
</FormItem>
<FormItem label="Last Name" colon={false}>
<Input placeholder = "Enter Your Last Name" style={{ maxWidth: '100%', maxHeight: '50%' }}
value = { lastname }
onChange = {
(e) => setLastName(e.target.value)
}/>
</FormItem>
<FormItem label="Company Name" colon={false}> <
Input placeholder = "Enter Your Company name." style={{ maxWidth: '100%', maxHeight: '50%' }}
value = { companyname }
onChange = {
(e) => setCompanyName(e.target.value)
}
/ >
</FormItem> */}
<FormItem>
<Button type="primary" htmlType="submit" id="submitButton">Register</Button>
</FormItem>
</Form>
);
}
}
const WrappedRegistrationForm = Form.create()(RegistrationForm);
export default withRouter(WrappedRegistrationForm)
am I missing something to actually render the elements on page?! All my other pages are rendering fine, just not this one! Any suggestions?

Modify an array of elements

I have an application where users can add their data in a form with 2 fields. They can add as many fields as they want.
const Demo = () => {
const onFinish = values => {
console.log("Received values of form:", values);
};
const firstDefaultOpen = {
name: 0,
key: 0,
isListField: true,
fieldKey: 0
};
const testHandler = a => {
console.log("result", a.concat(firstDefaultOpen));
return a.concat(firstDefaultOpen);
};
return (
<Form name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off">
<Form.List name="users">
{(fields, { add, remove }) => {
return (
<div>
{testHandler(fields).map(field => (
<Space
key={field.key}
style={{ display: "flex", marginBottom: 8 }}
align="start"
>
<Form.Item
{...field}
name={[field.name, "first"]}
fieldKey={[field.fieldKey, "first"]}
rules={[{ required: true, message: "Missing first name" }]}
>
<Input placeholder="First Name" />
</Form.Item>
<Form.Item
{...field}
name={[field.name, "last"]}
fieldKey={[field.fieldKey, "last"]}
rules={[{ required: true, message: "Missing last name" }]}
>
<Input placeholder="Last Name" />
</Form.Item>
<MinusCircleOutlined
onClick={() => {
remove(field.name);
}}
/>
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
block
>
<PlusOutlined /> Add field
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
My target is to set a default pair of inputs as opened. Now you can see when you open the application that first name and last name inputs are open as default. This i made with:
const firstDefaultOpen = {
name: 0,
key: 0,
isListField: true,
fieldKey: 0
};
const testHandler = a => {
console.log("result", a.concat(firstDefaultOpen));
return a.concat(firstDefaultOpen);
};
and here i map() the new array:
{testHandler(fields).map(field => (...
The issue is when i click on Add field button, because there when i try to write something in one input also the same text appears on the another. This is happening because when i click on Add field button you can see in the console.log("result", a.concat(firstDefaultOpen));, that 2 objects are with the same values like:
[Object, Object]
0: Object
name: 0
key: 0
isListField: true
fieldKey: 0
1: Object
name: 0
key: 0
isListField: true
fieldKey: 0
Question: How to set the first object with all values 0, and the next values to be higher, and to get something like?:
[Object, Object]
0: Object
name: 0
key: 0
isListField: true
fieldKey: 0
1: Object
name: 1
key: 1
isListField: true
fieldKey: 1
2: Object
name: 2
key: 2
isListField: true
fieldKey: 2
...
demo: https://codesandbox.io/s/hungry-star-nu5ld?file=/index.js:783-819
You can do this by just simply changing your firstDefaultOption from variable to function like this
function firstDefaultOption(optionVal){
return {
name: optionVal,
key: optionVal,
isListField: optionVal,
fieldKey: optionVal
}
}
and then changing your test handler like this
const testHandler = a => {
console.log("A",a)
console.log("result", a.concat(firstDefaultOption(a.length)));
return a.concat(firstDefaultOption(a.length));
};
Kind of a messy solution but it works... Comments added throughout
import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Form, Input, Button, Space } from "antd";
import { MinusCircleOutlined, PlusOutlined } from "#ant-design/icons";
const Demo = () => {
const onFinish = values => {
console.log("Received values of form:", values);
};
const firstDefaultOpen = {
//Set default to null
name: null,
key: null,
isListField: true,
fieldKey: null
};
const testHandler = a => {
//Check for null and set first to 0
if(firstDefaultOpen.name == null){
firstDefaultOpen.name = 0;
firstDefaultOpen.key = 0;
firstDefaultOpen.fieldKey = 0;
}
//If not null.. then add 1 to each object item to make each unique
else{
firstDefaultOpen.name = firstDefaultOpen.name +1;
firstDefaultOpen.key = firstDefaultOpen.key +1;
firstDefaultOpen.fieldKey = firstDefaultOpen.fieldKey +1;
}
console.log("result", a.concat(firstDefaultOpen));
return a.concat(firstDefaultOpen);
};
return (
<Form name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off">
<Form.List name="users">
{(fields, { add, remove }) => {
return (
<div>
{testHandler(fields).map(field => (
<Space
key={field.key}
style={{ display: "flex", marginBottom: 8 }}
align="start"
>
<Form.Item
{...field}
name={[field.name, "first"]}
fieldKey={[field.fieldKey, "first"]}
rules={[{ required: true, message: "Missing first name" }]}
>
<Input placeholder="First Name" />
</Form.Item>
<Form.Item
{...field}
name={[field.name, "last"]}
fieldKey={[field.fieldKey, "last"]}
rules={[{ required: true, message: "Missing last name" }]}
>
<Input placeholder="Last Name" />
</Form.Item>
<MinusCircleOutlined
onClick={() => {
remove(field.name);
}}
/>
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
block
>
<PlusOutlined /> Add field
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
ReactDOM.render(<Demo />, document.getElementById("container"));
You could declare the defaultOpen object with the let keyword and invoke an "update" function whenever you're ready to display an updated version, like:
let defaultOpen = getFirstDefaultOpen();
log(defaultOpen);
updateDefaultOpen(defaultOpen);
log(defaultOpen);
updateDefaultOpen(defaultOpen);
log(defaultOpen);
function updateDefaultOpen(currentState){
currentState.name++;
currentState.key++;
currentState.fieldKey++;
}
function getFirstDefaultOpen(){
return {
name: 0,
key: 0,
isListField: true,
fieldKey: 0
}
}
function log(currentState){
const output = Object.keys(currentState).reduce((str, key)=>{
str += `${key}:${currentState[key]}, `;
return str;
},"");
console.log(`{ ${output}}`);
}

after entering the value in the textbox if I hit enter, should see a new chip with the entered value in it

when I click textbox text a menu opens, in that textbox I need to enter some value in the textbox.
after entering the value in the textbox if I hit enter, I should see a new chip with the entered value in it.
so I thought I will create an Onchange event and pass the values to the chip component
but the values are not getting passed.
I think I need to pass values an object to that array.
chipData.push(handleTextBox);
console.log("handleTextBox after push chipData--->", chipData);
can you tell me how to fix it.
providing my code snippet and sandbox below.
https://codesandbox.io/s/material-demo-99sf8
demo.js
const [chipData, setChipData] = React.useState([
{ key: 0, label: "Angular" },
{ key: 1, label: "jQuery" },
{ key: 2, label: "Polymer" },
{ key: 3, label: "React" },
{ key: 4, label: "Vue.js" }
]);
const [hideChip, setHideChip] = React.useState([false]);
const handleClick = event => {
setAnchorEl({ type: "icon", target: event.currentTarget });
};
const handleClickFilter = event => {
setAnchorEl({ type: "textbox", target: event.currentTarget });
};
const handleTextBox = event => {
console.log("handleTextBox event--->", event);
console.log("handleTextBox event.target.value--->", event.target.value);
};
console.log("handleTextBox handleTextBox--->", handleTextBox);
console.log("handleTextBox chipData--->", chipData);
chipData.push(handleTextBox);
console.log("handleTextBox after push chipData--->", chipData);
<Menu
id="simple-menu"
anchorEl={
anchorEl && anchorEl.type === "textbox" && anchorEl.target
}
open={Boolean(anchorEl && anchorEl.type === "textbox")}
onClose={handleClose}
>
<MenuItem>
<form
className={classes.container}
noValidate
autoComplete="off"
>
<TextField
id="standard-name"
label="Name"
className={classes.textField}
onChange={handleTextBox}
// value={values.name}
// onChange={handleChange('name')}
margin="normal"
/>
</form>
</MenuItem>
</Menu>
<Paper className={classes.root}>
{chipData.map(data => {
let icon;
console.log("setHideChip--->", setHideChip);
if (data.label === "React") {
icon = <TagFacesIcon />;
}
return (
<Chip
key={data.key}
icon={icon}
label={data.label}
onDelete={handleDelete(data)}
className={classes.chip}
/>
);
})}
</Paper>
try with this code .
<TextField
fullWidth={true}
id="standard-name"
label="Tags"
className={classes.textField}
onChange={handleChange('tag')}
onKeyDown={(e) => pushToarray(e)}
margin="dense"
/>
on entering you should see tag being added
function pushToarray(e){
if(e.key == 'Enter'){
e.preventDefault();
setValues({ tags : [...values.tags ,e.target.value] ,
tag : ''})
}
}
and normal handleChange of text change in the box :
const handleChange = name => event => {
setValues({ ...values, [name]: event.target.value });
console.log(values)
};
Initialize state like this I have initialized with empty array you can use keys:
const [values, setValues] = React.useState({
tag :'',
tags: [],
})
here is your handleDelete function to remove chips :
function handleDelete(item){
var rm = values.tags.indexOf(item);
values.tags.splice( rm, 1)
setValues( { tags : values.tags})
}
this is how your chip component should be
<Paper className={classes.root}>
{
values.tags && values.tags.map( (tag ,id) =>(
<Chip color="default" className={classes.chip} onDelete={() => handleDelete(tag)} key={id} label={tag}/>
))
}

How to prevent setState changing input field?

I am using ant design form and trying to prevent clearing input after I check a checkbox.
Here is the code :
this.state = {
externalOptionsArray: [],
}
// onClick function
optionOnChange = (e, index, array) => {
const { externalOptionsArray } = this.state
let externalOptionCurrentState = externalOptionsArray
externalOptionCurrentState[index].is_enabled = !externalOptionCurrentState[index].is_enabled;
this.setState({
externalOptionsArray: externalOptionCurrentState
})
}
// Components
<Form onSubmit={this.onSubmit}>
<FormContainerWithDescription
direction="vertical"
title="Product"
description="Product Information">
<FormItemRow>
<Col span={24} style={colStyle}>
<FormItem label={'Product name'} colon={false} style={{ marginBottom: 0 }}>
{getFieldDecorator('title', {
rules: [
{ required: true, message: 'name is required' },
],
})(<Input />)}
</FormItem>
</Col>
</FormItemRow>
<FormItemRow>
<Col span={24} style={colStyle}>
<FormItem label={'external_options'} colon={ false } style={{ marginBottom: 0 }}>
{ externalOptionsArray.map((option, index, array) => {
return (
<Col style={{ float: 'left', width: '50%' }} key={option.name}>
<Row>
<Checkbox defaultChecked={option.is_enabled} onChange={() => this.optionOnChange(index, array)}>{option.name}</Checkbox>
</Row>
</Col>
)
})}
</FormItem>
</Col>
</FormItemRow>
</FormContainerWithDescription>
</Form>
And here is the image for better understanding
The problem is when I input a text in a input field and click one of checkboxes, input field automatically cleared up, it goes blank input field again.
I believe this is happening due to setState inside of optionOnChange function. Whenever I click one of checkboxes, setState takes place and it rerenders the DOM.
So I used e.preventDefault() and e.stopPropagaion inside of Checkbox component like this below.
<Checkbox defaultChecked={option.is_enabled} onChange={() => this.optionOnChange(index, array)} onClick={e => e.preventDefault()}>{option.name}</Checkbox>
// and
optionOnChange = (e, index, array) => {
e.preventDefault()
e.stopPropagation()
const { externalOptionsArray } = this.state
let externalOptionCurrentState = externalOptionsArray
externalOptionCurrentState[index].is_enabled = !externalOptionCurrentState[index].is_enabled;
this.setState({
externalOptionsArray: externalOptionCurrentState
})
}
But neither of them work
How can I make Checkbox works without rerender the input field?
You need to make it a controlled component. If you set the value of the Input component using a state variable then it remains even after state change.
So looking at your code above, you may need to have another variable like productName in the state and a onChange event handler to set the state when the input is changed
this.state = {
externalOptionsArray: [],
productName: '',
}
onChange = (e) => {
this.setState({productName: e.target.value});
}
And pass this to the Input like
<Input value={this.state.productName} onChange={this.onChange} />
You can store product's name in state and change it when input changes.
this.state = {
externalOptionsArray: [],
productName: '',
}
then define onChange function where you handle updating the state, like you did in optionOnChange()
and pass it to your Input component
<Input onChange={this.onChange} />

Categories

Resources