I'm having trouble with the dropdown that maps through the data I fetch from the Firebase realtime db and assigns selected value to the Formik form in order to save it to another object in the database.
I have restaurants object with pushId key containing name, city, and id (pushId key), and I have dishes with pushId key containing name, price, id (pushId key), restaurantId (id of the restaurant).
*Here I need another key:value pair that will be for example restaurantId: "-MCvTh91-qHLcy09ZpYm"
I did a fetch of restaurants using useEffect hook:
const [restaurants, setRestaurants] = useState([])
useEffect(() => {
let restQuery = Firebase.database().ref('restaurants').orderByKey()
restQuery.once('value').then(function(snapshot) {
snapshot.forEach(function(childSnapshot) {
setRestaurants(prevState => [...prevState, childSnapshot.val()])
})
})
},[])
But I simply can't map the restaurants array to the dropdown form options and if I select a restaurant save its id to the Formik value
<Formik
initialValues={{
name: '',
price: '',
restaurantId: '',
}}
onSubmit = {(data, {resetForm}) => {
console.log('submit: ', data)
resetForm()
}}>
{({values, handleChange, handleSubmit}) => (
<Form onSubmit={handleSubmit}>
<Field name="name" type="input" as={TextField} /><br/>
<Field name="price" type="input" as={TextField} /><br/>
<div>Restaurants:</div>
<Field type="select" as={Select}>
{restaurants.map((rest) =>
<MenuItem name="restaurantId" value={rest.id}>
{rest.name}
</MenuItem>
)}
</Field>
<div>
<Button type="submit">Submit</Button>
</div>
</Form>
)}
</Formik>
This gives mi selection of the restaurants in the dropdown like:
However, when I click on the option the error console throws these warnings and nothing happens
Warning: Formik called `handleChange`, but you forgot to pass an `id` or `name` attribute to your input: undefined
Formik cannot determine which value to update. For more info see https://github.com/jaredpalmer/formik#handlechange-e-reactchangeeventany--void
Material-UI: You have provided an out-of-range value `[object Object]` for the select component.
Consider providing a value that matches one of the available options or ''.
The available values are `-MCvTh91-qHLcyO9ZpYm`, `-MCxfsFz6pSaokkbBwpA`, `-MCxg6CB_U4HaCE659Tw`.
I'm stuck on these and anything I try doesn't work.
As answered by #Chris G in the comments the issue was in placement of the name attribute, it should be in the select element not the option element:
{({values, handleChange, handleSubmit}) => (
<Form onSubmit={handleSubmit}>
<Field name="name" type="input" as={TextField} /><br/>
<Field name="price" type="input" as={TextField} /><br/>
<div>Restaurants:</div>
<Field type="select" name="restaurantId" as={Select}>
{restaurants.map((rest) =>
<MenuItem key={rest.id} value={rest.id}>
{rest.name}
</MenuItem>
)}
</Field>
<div>
<Button type="submit">Submit</Button>
</div>
</Form>
)}
Related
I am using the Context API to add user details from a form to a global state. When I submit the form, the state is always "one step behind" - essentially, a double click is required to get the desired result.
Basic recreation of the code (have removed irrelevant bits and some imports):
import { UserProvider, UserContext } from "../../StateContext"
export default function SignUp() {
const user = useContext(UserContext)
const history = useHistory()
const handleSubmit = (e) => {
e.preventDefault()
user.setName(userDetails.name)
//logging out the user object here will result in the previous values shown
}
const [userDetails, setUserDetails] = useState({
name: null,
age: null,
})
return (
<>
<form onSubmit={handleSubmit}>
<div className="form-vertical-batch">
<FormControl>
<Input
type="text"
placeholder="Your Name"
required={true}
onChange={(e) =>
setUserDetails({ ...userDetails, name: e.target.value })
}
></Input>
<FormHelperText>
Put the name you're best known by online - either a nickname,
brand name, or your real name.
</FormHelperText>
</FormControl>
<FormControl>
<TextField
type="number"
inputProps={{ min: 18, max: 99 }}
onChange={(e) =>
setUserDetails({ ...userDetails, age: e.target.value })
}
/>
<FormHelperText>
You currently must be at least 18 years old to use the platform.
</FormHelperText>
</FormControl>
</div>
</div>
<input
id="formButton"
className="btn sign-up-button"
type="submit"
placeholder="Send message"
/>
</form>
</>
)
}
To clarify the issue here - if I submit with a name as "Reikon" and log our the user object, the first time it will return as null, and then the second time it will return "Reikon" as expected.
I try to save data from 2 form (Main and SubForm) using getFieldValue(). Here should appear both form data:
const save = () => {
console.log(myRef.current.getFieldValue());
};
Now i get an empty object when i click save handler. If i remove the <SubForm/> i get value from Main form, but if i add again the second form i ca not get data from both form.
,br> How to get data, clicking on save button, from both forms using getFieldValue()?
demo: https://codesandbox.io/s/dazzling-ganguly-vz7o7?file=/src/OuterForm.js
A part of my code:
<Form
name="main"
ref={myRef}
initialValues={{ remember: true }}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
id="ma"
form={form}
>
<Form.Item
label="Username"
name="username"
rules={[{ required: true, message: "Please input your username!" }]}
>
<Input />
</Form.Item>
<Form.Item
label="Password"
name="password"
rules={[{ required: true, message: "Please input your password!" }]}
>
<Input.Password />
</Form.Item>
</Form>
<SubForm myRef={myRef} />
<button onClick={save}>save</button>
</div>
You need to define 2 refs, one for each form.
const mainFormRef = useRef();
const subFormRef = useRef();
and set these refs on respective forms
<Form
name="main"
ref={mainFormRef}
....
>
....
</Form>
<SubForm myRef={subFormRef} />
finally, inside save function, call getFieldValue on both refs
const save = () => {
console.log(mainFormRef.current.getFieldValue());
console.log(subFormRef.current.getFieldValue());
};
I have a form in Reactjs where I am expected to collect the information and sent to my API. This information include, Phonenumber, gender, date of birth and country of birth.
I have written my react handleChange code as follows:
handleChange (event) {
const { name, value } = event.target;
const { info } = this.state;
this.setState({
info: {
...info,
[name]: value
}
});
}
Then I have written my return function as follows
return (
<CardBody>
<form name="form" onSubmit={this.handleSubmit}>
<div className="grey-text">
<PhoneInput
placeholder="Enter phone number"
value={ info.phone_number }
onChange={this.handleChange}
error={ info.phone_number ? (isValidPhoneNumber(info.phone_number) ? undefined : 'Invalid phone number') : 'Phone number required' }/>
<Select
value={info.gender}
onChange={this.handleChange}
options={sex}
/>
<DatePicker
selected={info.dbirth}
onChange={this.handleChange}
peekNextMonth
showMonthDropdown
showYearDropdown
dropdownMode="select"
/>
<CountryDropdown
value={info.nationality}
onChange={this.onChangeh} />
</div>
<div style={{backgroundColor:"#006400"}}>
<Button type="submit" onClick={this.handleSubmit} className=" btn-success btn-block" >Validate Account</Button>
</div>
</form>
</CardBody>
);
}
};
When I try compile my project it compiles well but when I try to select any of the dropdowns such as phone number, gender I get the error
TypeError: event is undefined or TypeError: _event$target is undefined
I am quite new to reactjs. I have tried looking for answer to no avail . Any help on this will be appreciated
you're getting the error because the <Select /> will pass the value to the onChange unlike the <Input /> which passes an event , one way to get around this it to pass a similar object :
<Select
value={info.gender}
onChange={value => this.handleChange({target : {name : 'mySelect', value }}) }
options={sex}
/>
( probably the same for the datePicker )
You can simply define any variable in state. I defined selectedOption and used this code. This should help
handleSelect = (selectedOption) => {
this.setState({
selectedOption
})
}
and call in onChange={this.handleSelect}
I'm trying to capitalize the first letter of the input of my redux field. This is my code
const renderField = ({input, className, label, type, meta: {error, touched, submitFailed}}) => {
return (<div>
<div>
<input {...input}
className={className + ' ' + classNames(submitFailed && ((error && 'empty_field empty_placeholder')))}
placeholder={label} id={label} type={type}/>
</div>
</div>)
};
const SignUp = ({handleSubmit, emailSignUp, signUpStatus}) => {
return (
<form onSubmit={handleSubmit(emailSignUp)}>
<div className="width_100">
<div className="float_left text_align_left landing_name_cont">
<Field
className="landing_input"
name="signUpFirstName"
type="text"
maxLength={5}
component={renderField}
label="First name"
validate={[required]}
/>
</div>
</form>
)
};
export default reduxForm({
form: 'signUp'
})(SignUp)
There are many examples to do this when there's a normal input field but lacks examples on redux fields. How can i capitalize the first letter of a redux field?
I guess what you are looking for is manipulating the user input. You can use normalize in this situation.
There is a good example in the Redux docs: https://redux-form.com/7.1.2/examples/normalizing/
const capitalize = value => value.charAt(0).toUpperCase() + value.slice(1)
<Field
name="username"
component="input"
type="text"
placeholder="Username"
normalize={capitalize}
/>
I am using redux-form 7.0.4, it its working fine other than the following issue.
New user can enter details and existing user can edit details on the same form. I am having issue when existing user edit details and remove data from form field. I want to add check onBlur. If user empty form field and there is blur event I need to fill the initial data inside field.
Restore the initial/server value on blur if field is empty.
Form Field:
<Field
component={InputField}
type='text'
name='registeredName'
className='form-control'
required
placeholder='Registered Business Name*'
/>
InputField Component:
const InputField = ({
input,
type,
placeholder,
meta: { touched, error, initial }
}) => {
const onChange = (e) => {
const val = e.target.value;
if (type === 'phoneNumber') {
if (!/^\d+$/.test(val) || String(val).length > 10) {
return;
}
}
input.onChange(e);
};
return (
<div className='form-group'>
<input
{...input}
onChange={onChange}
type={type}
className='form-control'
placeholder={placeholder}
value={input.value }
onBlur={e => {input.value = (!e.target.value) ? initial : e.target.value;}}
/>
{touched &&
((error &&
<div className='error text-danger'>
{error}
</div>
))}
</div>
);
};
/* eslint-enable */
export default InputField;
You need to set onChange and onBlur in the Field component like this:
<Field
component={InputField}
type='text'
name='registeredName'
className='form-control'
required
placeholder='Registered Business Name*'
onChange={(event) => {
...your onChange function here...
}}
onBlur={(event) => {
...Your onBlur function here...
}}
/>
I'd recommend having a good read through the docs for the <Field /> component.
Hope that's helpful