React dropdown with options from Firebase realtime db (Formik + materialUI) - javascript

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

React Context Updates Not Firing During OnSubmit Functions?

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.

save data from many forms using getFieldValue()

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());
};

Not able to receive data in the handlechange in React JS

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}

Capitalizing first letter of input in Redux fields

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}
/>

Using onBlur with redux-form

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

Categories

Resources