I have a problem with the extraction of my input values onChange in my higher order component.
I want to assign input values to my contactFormData objectc properties.
Code:
export default withReflex()(ContactForm)
function ContactForm() {
let contactFormData = {
subject: '',
email: '',
message: '',
}
return (
<Section style={styles.container}>
<SectionTitle>
Contact Us
</SectionTitle>
<SectionSubtitle>
And let's code together
</SectionSubtitle>
<Flex col={12} justify="center" wrap style={{ margin: 'auto' }}>
<Flex p={2} col={12} md={6}>
<input
style={styles.input}
type="text" name="subject"
placeholder="Subject"
onChange={(subjectInput) => contactFormData.subject = subjectInput}
required
/>
</Flex>
<Flex p={2} col={12} md={6}>
<input
style={styles.input}
type="email"
name="from"
placeholder="Email"
onChange={(emailInput) => contactFormData.email = emailInput}
required />
</Flex>
<Flex p={2} col={12}>
<textarea
style={{ ...styles.input, height: 250 }}
placeholder="Message"
name="body"
onChange={(messageInput) => contactFormData.message = messageInput}
required
/>
</Flex>
<Flex p={2} col={12} justify="center">
<Button
typeStyle="primary"
style={{ width: '100%' }}
onClick={sendContactMail}
>
SEND THE MESSAGE
</Button>
</Flex>
</Flex>
</Section>
)
function sendContactMail() {
console.log(contactFormData);
}
}
When i console.log my object all values are "proxy" insted of expected values
Console:
use
onChange={(event) => contactFormData.subject = event.target.value;
Related
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} />
A have some form field that is on the Normal Page and on a Modal, i want the Modal button to trigger the submit event. Whenever i use the submit type="submit" on the Modal button, i get no resposnse. But if i use it in the Normal page button it works. Please what's going on, can't i use it in the Modal.
<Formik
initialValues={{
productName: "",
productNumber: "",
quantity: 1,
unitCost: 0,
totalCost: 0,
customerName: "",
customerNumber: "",
amount: "",
}}
onSubmit={(data, { setSubmitting }) => {
setSubmitting(true);
setTimeout(() => {
console.log(data);
alert(JSON.stringify(data, null, 2));
setSubmitting(false);
}, 2000);
}}
>
{({
values,
handleBlur,
handleChange,
setFieldValue,
isSubmitting,
}) => (
<Form>
<Box>
<Grid templateColumns="repeat(2, 1fr)" gap={5}>
<Box>
<FormLabel>Product Name:</FormLabel>
<Field
as={Input}
name="productName"
type="text"
placeholder="Product Name:"
/>
</Box>
<Box>
<FormLabel>Product Number:</FormLabel>
<Field
as={Input}
name="productNumber"
type="number"
placeholder="Product Number:"
/>
</Box>
<Box>
<FormLabel>Quantity:</FormLabel>
<Field
as={Input}
name="quantity"
type="number"
value={values.quantity}
onChange={handleChange}
onBlur={handleBlur}
placeholder="Quantity:"
/>
</Box>
<Box>
<FormLabel>Unit Cost:(in Naira)</FormLabel>
<Field
as={Input}
name="unitCost"
type="number"
value={values.unitCost}
onChange={handleChange}
onBlur={handleBlur}
placeholder="Unit Cost:"
/>
</Box>
<Box>
<FormLabel>Total Cost:(in Naira)</FormLabel>
<CalculatedField
type="number"
name="totalCost"
values={values}
value={values.totalCost}
setFieldValue={setFieldValue}
onChange={handleChange}
onBlur={handleBlur}
/>
</Box>
</Grid>
<Button mt={4} colorScheme="green" onClick={openIt} isFullWidth >
// If used here, it will work... but i don't want to use it here
Order Now
</Button>
</Box>
<Modal closeOnOverlayClick={true} isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Modal Title</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Box>
<Grid templateColumns="repeat(2, 1fr)" gap={5}>
<Box>
<FormLabel>Customer Name: </FormLabel>
<Field
as={Input}
name="customerName"
type="text"
placeholder="Customer Name:"
/>
</Box>
<Box>
<FormLabel>Customer Number: </FormLabel>
<Field
as={Input}
name="customerNumber"
type="text"
placeholder="Customer Number:"
/>
</Box>
<Box>
<FormLabel>Amount: </FormLabel>
<Field
as={Input}
name="amount"
type="text"
placeholder="Amount:"
/>
</Box>
</Grid>
</Box>
</ModalBody>
<ModalFooter>
<Button colorScheme="green" type="submit" >
// i want to use it here
Pay Now
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</Form>
)}
</Formik>
I don't really understand what you want exactly, but if you want to trigger the submit, you could do with your normal button.
First of all, pass submitForm or handleSubmit as props with your others ones
{({
submitForm or handleSubmit,
values,
handleBlur,
handleChange,
setFieldValue,
isSubmitting,
}) => (
Then on your butotn, remove the type submit and add an event handler on click
<Button colorScheme="green" onClick={submitForm // or handleSubmit}>
Pay Now
</Button>
Im sorry i don't know 100% which one will work, it's been a while im not using React an formik.
But remember, when something not working like you want, try to thing another way to do it. Typically trigger the submit manually with an event handler
I have a dynamic form where users can add data. By default i want to set the form in this way:
How you can see, the first pair of input is open when user opens the application.
I made this by changing this:
const Demo = () => {
const onFinish = values => {
console.log("Received values of form:", values);
};
return (
<Form name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off">
<Form.List name="users">
{(fields, { add, remove }) => {
console.log(fields);
return (
<div>
{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>
);
};
to this:
const Demo = () => {
const onFinish = values => {
console.log("Received values of form:", values);
};
const firstDefaultOpen = {
name: 0,
key: 0,
isListField: true,
fieldKey: 0
};
return (
<Form name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off">
<Form.List name="users">
{(fields, { add, remove }) => {
console.log(fields);
return (
<div>
{fields.concat(firstDefaultOpen).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>
);
};
Issue: But appears a problem when i try to add another input, because writing on first pair of inputs i can see the same text on the next inputs. Probably this is hapenening because of the same ids.
Question: How to set default open first pair of input as in the image using the code above?
demo: https://codesandbox.io/s/hungry-star-nu5ld?file=/index.js:235-2322
ps: i know that exists the next solution:
<Form initialValues={{ users: [""] }} name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off">
<Form.List name="users">
{(fields, { add, remove }) => {
console.log(fields);
return (
<div>
{fields.map(field => (
...
...
But i don't want this solution because i can't interact with <Form/> tag in my real application, so even this could be a solution, but i need another, like mine with concat() but of course to be workable.
I have the next application:
https://codesandbox.io/s/suspicious-murdock-87j13?file=/SubForm.js
<Form.List name="names">
{(fields, { add, remove }) => {
return (
<div>
{fields.map((field, index) => {
console.log("m", field);
return (
<Form.Item
{...(index === 0
? formItemLayout
: formItemLayoutWithOutLabel)}
label={index === 0 ? "Passengers" : ""}
required={false}
key={index}
>
<Form.Item
{...field}
name={[field.name, "outer"]}
fieldKey={[field.fieldKey, "outer"]}
validateTrigger={["onChange", "onBlur"]}
rules={[
{
required: true,
whitespace: true,
message:
"Please input passenger's name or delete this field."
}
]}
noStyle
>
<Input
placeholder="passenger name"
style={{ width: "60%" }}
/>
</Form.Item>
<Form.Item
{...field}
name={[field.name, "outer1"]}
fieldKey={[field.fieldKey, "outer1"]}
validateTrigger={["onChange", "onBlur"]}
rules={[
{
required: true,
whitespace: true,
message:
"Please input passenger's name or delete this field."
}
]}
noStyle
>
<Input
placeholder="passenger name"
style={{ width: "60%" }}
/>
</Form.Item>
<Form.Item>
<InnerForm fieldKey={field.key} />
</Form.Item>
{fields.length > 1 ? (
<MinusCircleOutlined
className="dynamic-delete-button"
style={{ margin: "0 8px" }}
onClick={() => {
remove(field.name);
}}
/>
) : null}
</Form.Item>
);
})}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "60%" }}
>
<PlusOutlined /> Add field
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
When user click on Add field button, in console appears the warning:
Warning: Encountered two children with the same key, `0`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
Also this warning appears when i click on Add sub-field. I can't figure out why this warning appears.
Question: Why the above warning appears and how to remove it?
You have two Form.Item Component in your code, and they use same key index, try to use different key like 'item1' + index and 'item2' + index.
And, in your index.js file also have same problem.
Here is Demo forked from you: https://codesandbox.io/s/dark-darkness-xz0wf?file=/SubForm.js
As reactJs primary works on re rendering items every time you make a change the state of that component. It requires an unique key to track each and every item so that re-rendering can be optimized. Not all items needs to be re-render every time.
This warning essentially means that you have multiple items with same key names. In order to fix this you have to provide unique "key" attribute to each child component which is getting updated, components which can potentially trigger an re-render.
you don't need to use multiple times key={index} or key={"g"}
use once only.
Please try following
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);
};
return (
<Form name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off">
<Form.List name="users">
{(fields, { add, remove }) => {
return (
<div>
{fields.map(field => (
<Space key={field.key} style={{ display: 'flex', marginBottom: 8 }} align="start">
<Form.Item
{...field}
name={[field.name, 'innerName']}
fieldKey={[field.fieldKey, 'innerName']}
>
<Input placeholder="passenger 1" />
</Form.Item>
<Form.Item
{...field}
name={[field.name, 'innerName2']}
fieldKey={[field.fieldKey, 'innerName2']}
>
<Input placeholder="passenger 2" />
</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'));
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 });