Upon submitting the form, I am url encoding the values to then send them in an API request. But when I encode the values, they show up on my form encoded:
Why is it changing the formik values? I'm trying to clone the object which I am encoding in order to not update the formik values but they still update.
Code:
export const uploadAddProductEpic: Epic<*, *, *> = (
action$: ActionsObservable<*>
) =>
action$.ofType(UPLOAD_ADD_PRODUCT).mergeMap((action) => {
const newAction = Object.assign({}, encodeAddProductAction(action))
return ajax
.post('http://192.168.1.12:4000/products', newAction.payload, {
'Content-': 'application/json'
})
.map((response) => uploadAddProductFulfilled(response))
.catch((error) => Observable.of(uploadAddProductRejected(error)))
})
export const handleSubmit = (values, props: AddViewProps): void => {
Toast.show('Uploading Product', {
duration: 3000,
position: 30,
shadow: true,
animation: true,
hideOnPress: true,
delay: 0
})
props.uploadAddProduct(values)
}
export const encodeAddProductAction = (action: VepoAction<Product>) => {
const action1 = Object.assign({}, action)
action1.payload.Shop = encodeURIComponent(
JSON.stringify(action1.payload.Shop)
)
action1.payload.Brand = encodeURIComponent(
JSON.stringify(action1.payload.Brand)
)
action1.payload.Name = encodeURIComponent(
JSON.stringify(action1.payload.Name)
)
action1.payload.Description = encodeURIComponent(
JSON.stringify(action1.payload.Description)
)
return action1
}
const mapStateToProps = (state: RecordOf<VepoState>) => ({
locationListDisplayed: state.formControls.root.locationListDisplayed
})
// eslint-disable-next-line flowtype/no-weak-types
const mapDispatchToProps = (dispatch: Dispatch<*>): Object => ({
uploadAddProduct: (product: Product): void => {
dispatch(uploadAddProduct(product))
}
})
class AddGroceryItemView extends React.Component {
render() {
const {
values,
handleSubmit,
setFieldValue,
errors,
touched,
setFieldTouched,
isValid,
isSubmitting
} = this.props
return (
<Container>
<VepoHeader title={'Add Vegan Grocery Product'} />
<Container style={container}>
<ScrollView
keyboardShouldPersistTaps="always"
style={viewStyle(this.props.locationListDisplayed).scrollView}>
<LocationAutocomplete
label={'Grocery Store'}
placeholder={'Enter Grocery Store'}
setFieldTouched={setFieldTouched}
setFieldValue={setFieldValue}
name="GroceryStore"
required
error={errors.GroceryStore}
touched={touched.GroceryStore}
/>
<View style={viewStyle().detailsContainer}>
<Input
label={'Product Name'}
onTouch={setFieldTouched}
value={values.Name}
placeholder="Enter Name"
name="Name"
required
error={touched.Name && errors.Name}
deleteText={setFieldValue}
onChange={setFieldValue}
/>
<Input
label={'Product Brand'}
value={values.Brand}
onTouch={setFieldTouched}
error={touched.Brand && errors.Brand}
placeholder="Enter Brand"
name="Brand"
required
onChange={setFieldValue}
deleteText={setFieldValue}
/>
<View>
<Input
label={'Product Description'}
value={values.Description}
placeholder="Enter Description"
multiline={true}
required
onTouch={setFieldTouched}
error={touched.Description && errors.Description}
numberOfLines={4}
name="Description"
deleteText={setFieldValue}
onChange={setFieldValue}
/>
<Input
isValid={true}
isPrice={true}
label={'Product Price'}
value={values.Price}
onTouch={setFieldTouched}
error={touched.Price && errors.Price}
placeholder="Enter Price"
name="Price"
deleteText={setFieldValue}
onChange={setFieldValue}
/>
<CategoriesMultiselect.View
error={errors.Categories}
setFieldValue={setFieldValue}
setFieldTouched={setFieldTouched}
touched={touched.Categories}
name="Categories"
required
label="Product Categories"
categoryCodes={[CategoryEnums.CategoryCodes.Grocery]}
/>
<ImagePicker
label="Product Image"
setFieldValue={setFieldValue}
name="Image"
/>
</View>
</View>
</ScrollView>
</Container>
<Button.View
title="submit"
onPress={handleSubmit}
label={'GO!'}
disabled={!isValid || isSubmitting}
loading={isSubmitting}
/>
</Container>
)
}
}
const container = {
flex: 1,
...Spacing.horiz_pad_md_2,
backgroundColor: Colors.grey_lite,
flexDirection: 'column'
}
const formikEnhancer = withFormik({
validationSchema: Yup.object().shape({
Name: Yup.string().required(),
Brand: Yup.string().required(),
GroceryStore: Yup.object()
.shape({
city: Yup.string(),
latitude: Yup.number(),
longitude: Yup.number(),
name: Yup.string(),
place_id: Yup.string(),
street: Yup.string(),
street_number: Yup.string(),
suburb: Yup.string()
})
.required(),
Image: Yup.object().shape({
uri: Yup.string(),
name: Yup.string(),
type: Yup.string()
}),
Categories: Yup.array()
.min(1, 'Please select at least 1 Category')
.required(),
Description: Yup.string()
.min(9)
.required(),
Price: Yup.string().matches(
/^\d+(?:\.\d{2})$/,
'Price must contain 2 decimal places (cents) e.g. 4.00'
)
}),
isInitialValid: false,
mapPropsToValues: () => ({
Name: '',
Brand: '',
Description: '',
Price: '',
Categories: [],
GroceryStore: {},
Image: {}
}),
handleSubmit: (values, { props }) => {
handleSubmit(values, props)
},
displayName: 'AddGroceryItemView'
})(AddGroceryItemView)
Related
I'm strugling with my form in typescript react. Basicly I created 2 simple components Button and Input and add them to the another component Form where I created a whole form.
After submit I should get something like this:
telephone: "323423234"
email: "tress#wess.com"
message: "weklfnwlkf"
name: "less"
surname: "mess"
But after submit I get weird response like this:
"": "323423234"
email: "tress#wess.com"
message: "weklfnwlkf"
name: "less"
surname: "mess"
telephone: ""
It is a little bit werido because I done something like this couple days ago and now idk what to do.
There is sample of my Form code
const Form = () => {
interface FormDataType {
telephone: string;
name: string;
surname: string;
email: string;
message: string;
}
const formData: FormDataType = {
telephone: '',
name: '',
surname: '',
email: '',
message: '',
};
const [responseBody, setResponseBody] = useState<FormDataType>(formData);
const clearState = () => {
setResponseBody({ ...formData });
};
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = event.target;
setResponseBody({ ...responseBody, [name]: value });
};
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
console.log(responseBody);
clearState();
};
return (
<>
<form onSubmit={handleSubmit}>
{FormData.map((option) => (
<Input
key={option.name}
label={option.label}
type={option.type}
className={option.className}
name={option.name}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
handleChange(e)
}
/>
))}
<div className='div'>
<Button
type='submit'
title={'Simple test'}
className={'btn-primary'}
/>
</div>
</form>
</>
);
};
export default Form;
And when I'm invoking in handleSubmit function clearState() it doesn't refresh states in webpage inputs.
I hope this is what it needs, I made some changes, but you can adapt it to your project
export default function App() {
interface FormDataType {
telephone: string;
name: string;
surname: string;
email: string;
message: string;
}
const formData: FormDataType = {
telephone: "",
name: "",
surname: "",
email: "",
message: ""
};
const [values, setValues] = useState<FormDataType | any>(formData);
// reset
const reset = (newFormState = formData) => {
setValues(newFormState);
};
// handleInputChange
const handleInputChange = ({ target }: any) => {
setValues({ ...values, [target.name]: target.value });
};
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
console.log(values);
reset();
};
const data = [
{
type: "text",
name: "name"
},
{
type: "text",
name: "surname"
},
{
type: "email",
name: "email"
},
{
type: "tel",
name: "telephone"
},
{
type: "text",
name: "message"
}
];
return (
<>
<form onSubmit={handleSubmit}>
{data.map((option) => (
<input
key={option.name}
value={values[option.name]}
type={option.type}
name={option.name}
placeholder={option.name}
onChange={handleInputChange}
/>
))}
<div className="div">
<button type="submit">Simple test</button>
</div>
</form>
</>
);
}
Your reset doesn't work because your inputs are uncontrolled.
To have them reset properly, you should make them controlled by passing them a value prop. It would look something like this:
<Input
key={option.name}
label={option.label}
type={option.type}
className={option.className}
name={option.name}
value={responseBody[option.name]} // Add this
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
handleChange(e)
}
/>
This will connect the responseBody form data with the input values.
As to the odd console logged value, it would appear that at least one of the option.name values is empty/undefined (looks like the telephone object). Verify that the FormData contains complete and correct values.
I try to render Data from a json URL into an inputs. the Data is rendered correctly the Problem is I don't know how to check if the input is required or not you can check in Json Data bellow that the Array [from] have 5 objects and each one of this 5 objects have a key required who have a value of true or false
Well I have 2 questions:
How to verify if the fieldvalue is required or not?
How to send All the Data entered into the Form to an API url. Check the submitData() function
The Json Data:
{
"title":"Contact us",
"subtitle":"Enter your details to contact us",
"action":"form",
"configuration":[
{
"title":"Contact us",
"subtitle":"Enter your details to contact us",
"type":"action",
"actiontype":"form",
"posturl":"https://flow.simpas.ai/interview/content/123456/businesscard/",
"form":[
{
"fieldtype":"field",
"title":"First Name",
"fieldname":"firstname",
"placeholder":"Enter your first name",
"iconurl":"https://www.creativefabrica.com/wp-content/uploads/2019/02/People-Icon-by-Kanggraphic-1-580x386.jpg",
"fieldvalue":"",
"required":true
},
{
"fieldtype":"field",
"title":"Last Name",
"fieldname":"latstname",
"placeholder":"Enter your last name",
"iconurl":"https://www.creativefabrica.com/wp-content/uploads/2019/02/People-Icon-by-Kanggraphic-1-580x386.jpg",
"fieldvalue":"",
"required":true
},
{
"fieldtype":"email",
"title":"Email",
"fieldname":"email",
"placeholder":"Enter your email",
"iconurl":"data:image/png;base64,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",
"fieldvalue":"",
"required":true
},
{
"fieldtype":"textarea",
"title":"Note",
"fieldname":"note",
"placeholder":"Enter your note",
"iconurl":"data:image/png;base64,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",
"fieldvalue":"",
"required":true
}
]
}
]
}
The Code:
import React, { useState,useEffect } from 'react';
import {
View,
Text,
TextInput,
SafeAreaView,
Keyboard,
ScrollView,
Alert,
} from 'react-native';
import COLORS from '../src/conts/colors';
import Button from '../src/views/components/Button';
import Input from '../src/views/components/Input';
import Loader from '../src/views/components/Loader';
const ContactForm = ({navigation}) => {
const [inputs, setInputs] = React.useState({
firstname: '',
lastname: '',
email: '',
note: '',
});
const [errors, setErrors] = React.useState({});
const [loading, setLoading] = React.useState(false);
const validate = () => {
Keyboard.dismiss();
let isValid = true;
if (!inputs.firstname) {
handleError('Please input first name', 'firstname');
isValid = false;
}
if (!inputs.lastname) {
handleError('Please input last name', 'lastname');
isValid = false;
}
if (!inputs.email) {
handleError('Please input email', 'email');
isValid = false;
} else if (!inputs.email.match(/\S+#\S+\.\S+/)) {
handleError('Please input a valid email', 'email');
isValid = false;
}
if (!inputs.note) {
handleError('Please input note', 'note');
isValid = false;
}
if (isValid) {
submitData();
}
};
const submitData = ()=>{
fetch("https://flow.simpas.ai:2021/react/contact",{
method:"post",
headers:{
'Content-Type': 'application/json'
},
body: JSON.stringify({
firstname: inputs.firstname,
lastname: inputs.lastname,
email: inputs.email,
note: inputs.note,
})
})
.then(res=>res.json())
.then(data=>{
alert(`${data.firstname} is saved successfuly`);
navigation.navigate("Home")
})
.catch(err=>{
alert("someting went wrong")
})
};
const handleOnchange = (text, input) => {
setInputs(prevState => ({...prevState, [input]: text}));
};
const handleError = (error, input) => {
setErrors(prevState => ({...prevState, [input]: error}));
};
const [data, getData] = useState([])
const URL = "https://flow.simpas.ai:2021/react/contact";
useEffect(() => {
fetchData()
}, [])
const fetchData = () => {
fetch(URL)
.then((res) =>
res.json())
.then((response) => {
console.log(response);
getData(response.configuration?.[0]);
})
}
return (
<SafeAreaView style={{ flex: 1}}>
<Loader visible={loading} />
<ScrollView
contentContainerStyle={{paddingTop: 50, paddingHorizontal: 20}}>
<Text style={{color: COLORS.black, fontSize: 40, fontWeight: 'bold',fontFamily: 'Roboto',textAlign: 'center'}}>
{data?.title}
</Text>
<Text style={{color: COLORS.grey, fontSize: 18, marginVertical: 10,fontFamily: 'Roboto',textAlign: 'center'}}>
{data?.subtitle}
</Text>
<View style={{ marginVertical: 20 }}>
{data?.form?.map?.((item, i) => {
return (
<Input key={i}
onChangeText={(text) =>
handleOnchange(text, item.fieldname)
}
onFocus={() =>
handleError(null, item.fieldname)
}
iconName="account-outline"
label={item.title}
placeholder={item.placeholder}
/>
);
})}
<Button title="Contact Us" onPress={validate} />
</View>
</ScrollView>
</SafeAreaView>
);
};
export default ContactForm;
The output look like this:
I think the best answer for You would be to use react-hook-form. It's a library that provides simple props as: required field, min/max length, regex pattern for field.
It's pretty common used by many developers and companies. I really would consider using it now and in the future ;)
There's nice snippet that explains how the plugin works:
import React from "react";
import { useForm } from "react-hook-form";
const RegisterForm = () => {
const { register, handleSubmit, formState: { errors } } = useForm();
const handleRegistration = (data) => console.log(data);
const handleError = (errors) => {};
const registerOptions = {
name: { required: "Name is required" },
email: { required: "Email is required" },
password: {
required: "Password is required",
minLength: {
value: 8,
message: "Password must have at least 8 characters"
}
}
};
return (
<form onSubmit={handleSubmit(handleRegistration, handleError)}>
<div>
<label>Name</label>
<input name="name" type="text" {...register('name', registerOptions.name) }/>
<small className="text-danger">
{errors?.name && errors.name.message}
</small>
</div>
<div>
<label>Email</label>
<input
type="email"
name="email"
{...register('email', registerOptions.email)}
/>
<small className="text-danger">
{errors?.email && errors.email.message}
</small>
</div>
<div>
<label>Password</label>
<input
type="password"
name="password"
{...register('password', registerOptions.password)}
/>
<small className="text-danger">
{errors?.password && errors.password.message}
</small>
</div>
<button>Submit</button>
</form>
);
};
export default RegisterForm;
Below is the code where I am trying to select value from dropdown.
handleChange is not working, when I select the value from dropdown it's not getting updated with selected value from dropdown. It's getting vanished(blank).
Dropdown values are getting populated when I select it's not catching the new selected value.
Can someone help me on this like what I am missing here?
export const FormikSelectField = ({ label, ...props }) => {
const [field, meta] = useField(props);
const [isFocused, setOnFocus] = useState(false);
const handleChange = (value) => {
// this is going to call setFieldValue and manually update values.topcis
console.log('Value in handlechange..', value ,'and', props.name);
props.onChange(props.name, value.value);
};
const handleFocus = () => {
setOnFocus(true);
};
const handleBlur = (e) => {
// this is going to call setFieldTouched and manually update touched.topcis
setOnFocus(false);
props.onBlur(props.name, true);
field.onBlur(e);
};
return (
<>
<label htmlFor={props.labelName}>{props.labelName} </label>
<Select
id={props.labelName}
options={props.options}
isMulti={false}
onChange={handleChange}
onBlur={(e) => handleBlur(e)}
placeholder='Select an option'
onFocus={handleFocus}
value={props.value}
/>
{meta.touched && meta.error && !isFocused ? (
<div className="error">{meta.error}</div>
) : null}
</>
);
};
formikInitialValues = () => {
return {
Name: [{
title: '',
value: '',
}]
};
};
YupValidationSchema = () => {
return Yup.object({
Name: Yup.array()
.of(
Yup.object().shape({
title: Yup.string().required(),
value: Yup.string().required(),
})
)
.required("Please select an option")
.nullable()
});
};
<FormikSelectField
value={this.state.selectNameOption}
onChange={this.handleNameChange}
onBlur={this.handleNameChangeBlur}
error={formik.errors.Name}
options={this.state.NameOptions}
touched={formik.touched.Name}
name="Name"
labelName="Name"
/>
You should avoid mixing the state when using Formik. Formik will take care of the state for you.
import { Formik, Form, useField, ErrorMessage } from "formik";
import * as Yup from "yup";
import Select from "react-select";
const iceCreamOptions = [
{ value: "chocolate", label: "Chocolate" },
{ value: "strawberry", label: "Strawberry" },
{ value: "vanilla", label: "Vanilla" }
];
const FormSelect = ({ name, options }) => {
const [field, meta, helpers] = useField(name);
return (
<>
<Select
name={name}
value={field.value}
onChange={(value) => helpers.setValue(value)}
options={options}
onBlur={() => helpers.setTouched(true)}
/>
<ErrorMessage name={name} />
</>
);
};
const initialValues = {
icecream: null
};
const validationSchema = Yup.object().shape({
icecream: Yup.object()
.shape({
value: Yup.string(),
label: Yup.string()
})
.required("Please select a value")
.nullable()
});
export default function App() {
return (
<Formik
initialValues={initialValues}
onSubmit={(values) => console.log(values)}
validationSchema={validationSchema}
>
{(props) => {
return (
<Form>
<pre>{JSON.stringify(props, undefined, 2)}</pre>
<FormSelect name="icecream" options={iceCreamOptions} />
</Form>
);
}}
</Formik>
);
}
Example Working Sandbox
Write this code
onChange={(e:React.ChangeEvent<HTMLInputElement>)=> setFieldValue("title",e.target.value)}
or
onChange={(e)=> setFieldValue("title",e.target.value)}
I have a removeUser page where I am using a < Mutation > and then I am doing my error handling using the submitForm() function. This code worked perfectly well:
export default function RemoveUserPage() {
const [isSubmitted, setIsSubmitted] = useState(false);
const [isRemoved ,setIsRemoved] = useState(false);
const [errorMessage, setErrorMessage] = useState('');
function StatusMessage(){
if (isRemoved){
return (
<CustomAlert severity='success' text='User Removed'></CustomAlert>
)
}
//else...
}
function submitForm(RemoveUserMutation: any, email: string) {
setIsSubmitted(true);
RemoveUserMutation({
variables: {
email: email,
},
}).then(({ data }: any) => {
setIsRemoved(true);
})
.catch((error: { message: string; }) => {
setIsRemoved(false);
console.log("Error msg:" + error.message);
setErrorMessage(error.message)
})
}
return (
<Mutation mutation={RemoveUserMutation}
>
{(RemoveUserMutation: any) => (
<div>
<PermanentDrawerLeft></PermanentDrawerLeft>
<Formik
initialValues={{ email: '' }}
onSubmit={(values, actions) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}, 1000);
}}
validationSchema={schema}
>
{props => {
const {
values: { email },
errors,
touched,
handleChange,
isValid,
setFieldTouched
} = props;
const change = (name: string, e: any) => {
e.persist();
handleChange(e);
setFieldTouched(name, true, false);
};
return (
<div className='main-content'>
<form style={{ width: '100%' }}
onSubmit={e => {e.preventDefault();
submitForm(RemoveUserMutation, email)}}>
<div>
<TextField
variant="outlined"
margin="normal"
id="email"
name="email"
helperText={touched.email ? errors.email : ""}
error={touched.email && Boolean(errors.email)}
label="Email"
value={email}
onChange={change.bind(null, "email")}
/>
<br></br>
<Button
type="submit"
disabled={!isValid || !email}
>
Remove User</Button>
</div>
</form>
<br></br>
{isSubmitted && StatusMessage()}
</div>
)
}}
</Formik>
</div>
)}
</Mutation>
);
}
However, I was suggested to use useMutationinstead. Firstly, I am unable to do so since I get such errors:
Unhandled Rejection (Error): GraphQL error: Variable `email` of type `String!` must not be null.
And even if the mutation works, is there any way I can still modify and use the same function for error handling in my case?
This is what I was trying now but this doesn't work:
export default function RemoveUserPage() {
const [isSubmitted, setIsSubmitted] = useState(false);
const [isRemoved ,setIsRemoved] = useState(false);
const [errorMessage, setErrorMessage] = useState('');
const [removeUser] = useMutation(RemoveUserMutation);
function StatusMessage(){
if (isRemoved){
return (
<CustomAlert severity='success' text='User Removed'></CustomAlert>
)
}
}
function submitForm(RemoveUserMutation: any, email: string) {
setIsSubmitted(true);
RemoveUserMutation({
variables: {
email: email,
},
}).then(({ data }: any) => {
setIsRemoved(true);
})
.catch((error: { message: string; }) => {
setIsRemoved(false);
setErrorMessage(error.message)
})
}
return (
<div>
<PermanentDrawerLeft></PermanentDrawerLeft>
<Formik
initialValues={{ email: '' }}
onSubmit={(values, actions) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}, 1000);
}}
validationSchema={schema}
>
{props => {
const {
values: { email },
errors,
touched,
handleChange,
isValid,
setFieldTouched
} = props;
const change = (name: string, e: any) => {
e.persist();
handleChange(e);
setFieldTouched(name, true, false);
};
return (
<div className='main-content'>
<form style={{ width: '100%' }}
onSubmit={e => {e.preventDefault();
removeUser({variables: {todo: email }});}}>
<div>
<TextField
variant="outlined"
margin="normal"
id="email"
name="email"
helperText={touched.email ? errors.email : ""}
error={touched.email && Boolean(errors.email)}
label="Email"
value={email}
onChange={change.bind(null, "email")}
/>
<br></br>
<Button
type="submit"
disabled={!isValid || !email}
>
Remove User</Button>
</div>
</form>
<br></br>
{isSubmitted && StatusMessage()}
</div>
)
}}
</Formik>
</div>
);
}
There's no reason to have a RemoveUserMutation anymore -- removeUser is already in the scope, so just use it.
function submitForm(email: string) {
setIsSubmitted(true);
removeUser({
variables: {
email,
},
})
...
}
You can continue to use your submitForm function like this:
onSubmit={e => {
e.preventDefault();
submitForm(email);
}}
This line
removeUser({variables: {todo: email }})
isn't working because there is no todo variable. Since your using TypeScript, you should generate type definitions for your queries and then use them with the hooks. This would prevent mistakes like this.
I have a react component and I want to pass down onChange as a prop to a child component, I'm using atomic design, here's my code:
HeadComponent.js
class Headcomponent extends React.Component{
constructor (props) {
super(props);
this.state = {
email: '',
password: '',
formErrors: {email: '', password: ''},
emailValid: false,
passwordValid: false,
formValid: false,
items: [],
}
}
handleUserInput = (e) => {
const name = e.target.name;
const value = e.target.value;
this.setState({[name]: value},
() => { this.validateField(name, value) });
}
render(){
const fields= [
{
label: 'hhdghghd',
placeholder: 'fhfhhfhh 1',
ExampleMessage: this.state.formErrors.email ,
ErrorMessage: 'error message for input 1',
inputValue: this.state.email,
onChange: this.handleUserInput //this is where I'm passing my function
},
{
label: 'fffff',
placeholder: 'tttttt 2',
ExampleMessage: 'example message for second label',
ErrorMessage: 'error message for input 2',
onChange: this.handleUserInput
},
]
return (
<div>
<Form fields={fields} buttonText="Submit"/>
</div>
);
}
}
export default Headcomponent;
Form.js
const Form = props => (
<form className="Form">
{
props.fields.map((field, i) => (<LabeledInput label={field.label}
placeholder={field.placeholder}
ExampleMessage={field.ExampleMessage}
ErrorMessage={field.ErrorMessage}
onChange= {(e)=> field.onChange}
inputValue= {field.inputValue}
key={i}
passwordError={props.passwordError}
/>))
}
<Button text={props.buttonText} />
</form>
);
Form.propTypes = {
fields: PropTypes.arrayOf(PropTypes.object).isRequired,
buttonText: PropTypes.string.isRequired,
};
export default Form;
LabeledInput
const LabeledInput = props => (
<div className={`form-group `} >
<Label text={props.label} />
<Input inputValue={props.inputValue} placeholder={props.placeholder} type="text" onChange={(e)=> props.onChange} />
<ErrorMessage text={props.ErrorMessage} />
<ExampleMessage ExampleMessage={ props.ExampleMessage} />
</div>
);
LabeledInput.propTypes = {
label: PropTypes.string.isRequired,
placeholder: PropTypes.string,
onChange: PropTypes.func.isRequired,
//value: PropTypes.string.isRequired,
exampleText: PropTypes.string,
};
export default LabeledInput;
Input.js
const Input = props => (
<input type={props.type} class="form-control form-control-success is-valid" placeholder={props.placeholder} value={props.inputValue} className="form-control form-control-success"
onChange={ (e)=> props.onChange } />
);
Input.propTypes = {
inputValue: PropTypes.string,
type: PropTypes.string,
placeholder: PropTypes.string,
onChange: PropTypes.func.isRequired,
};
export default Input;
How to pass handleUserInput from HeadComponent.js down to Input.js, so far using props I can't get it to trigger while changing text.
You forgot to actually call field.onChange method. Instead of :
onChange= {(e)=> field.onChange}
You can change it to:
onChange= {field.onChange}
I also noticed that your handleUserInput set state at :
{ [name]: e.target.value }
However, you are not setting name to any of the inputs and that's not gonna work.
Please have a look at my working sample:
const LabeledInput = props => (
<div className="form-group">
<label>{props.label}</label>
<input
className="form-control"
type="text"
placeholder={props.placeholder}
onChange={props.onChange} // That's how you have to call onChange
name={props.name} // You are not setting this prop
/>
<div>{props.ErrorMessage}</div>
<div>{props.ExampleMessage}</div>
</div>
)
const Form = ({ buttonText, fields }) => (
<form className="Form">
{fields.map((field, i) => <LabeledInput key={i} {...field} />)}
<button className="btn btn-primary">{buttonText}</button>
</form>
)
class Headcomponent extends React.Component {
constructor() {
super()
this.state = {
email: '',
password: '',
formErrors: {email: '', password: ''},
emailValid: false,
passwordValid: false,
formValid: false,
items: [],
}
this.handleUserInput = this.handleUserInput.bind(this)
}
handleUserInput(e) {
const name = e.target.name
const value = e.target.value
// Now that you have `name` and `value`, state updates are going to work
this.setState({
[name]: value
})
}
render() {
const fields = [
{
label: 'Email',
placeholder: 'email placeholder',
ExampleMessage: this.state.formErrors.email ,
ErrorMessage: 'error message for input 1',
inputValue: this.state.email,
onChange: this.handleUserInput,
name: 'email',
},
{
label: 'Password',
placeholder: 'password placeholder',
ExampleMessage: 'example message for second label',
ErrorMessage: 'error message for input 2',
onChange: this.handleUserInput,
name: 'password',
},
]
return (
<div>
<Form fields={fields} buttonText="Submit"/>
<div style={{ marginTop: 20 }}>
<div>state.email: {this.state.email}</div>
<div>state.password: {this.state.password}</div>
</div>
</div>
)
}
}
ReactDOM.render(
<Headcomponent />,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<div id="root"></div>