I'm trying to render a select, and want to be able to do something like this to add options:
<Field component={RenderSelect} name="subjects" label="Subjects">
<option value="maths">Maths</option>
<option value="english">English</option>
</Field>
I have a createRenderer function:
const createRenderer = render => ({input, name, label, children}) => {
return (
<div key={name}>
<label htmlFor={name}>{label}</label>
{render(input, name, children)}
</div>
)
},
and my RenderSelect looks like this:
const RenderSelect = createRenderer((input, name, label, children) => {
return (
<select name={name} {...input}>
{children}
</select>
)
})
I was under the impression that I could just destructure the children prop off the Field like I do for input, name, label, etc, although this does not seem to work. When I run the code, no options appear in my select, and an inspection of the DOM verifies that there are no options. Any help would be much apprecited. Thanks in advance!
You just need to remove your label parameter
const RenderSelect = createRenderer((input, name, children) => {
return (
<select name={name} {...input}>
{children}
</select>
)
});
Related
I feel like there is a simple solution to this but I've been stuck on it for a while.
This is what I did:
export default function App() {
const [searchTerm, setSearchTerm] = useState('');
return (
<div>
<div>
<input type="text" onChange={(e) => setSearchTerm(e.target.value)} />
</div>
<select>
<option defaultOption>Choose option</option>
{SelectOptions.filter(({ val }) => val.includes(searchTerm)).map(
({ val, id }) => {
return (
<option key={id} value={id}>
{val}
</option>
);
}
)}
</select>
</div>
);
}
sandbox: https://stackblitz.com/edit/react-qx5fym?file=src%2FApp.js
As you can see I have an input above my select and it works. If I search something, the select option shorten the list. However, I'm struggling on trying to get the input inside the select.
I want to be able to get this:
Exclude the styling that isn't the goal right now.
Anyone know how to achieve this?
I am attempting to create a dynamic form in which there are 2 text fields and one dropdown select. These fields can be added by clicking the "Add More.." button. The remove button removes a particular field set. After an npm start the code shows all elements normally, add, remove and input fields work as intended. However, the problem starts when the select is used. On selecting something, the app crashes and gives a white screen with the errors [tag:"formFields.map is not a function"] and [tag:"Consider adding an error boundary to your tree to customize error handling behavior."] I would appreciate any help that can resolve this. :)
P.S. I am learning react through building projects rather than the conventional method of sitting through hours of tutorials and figuring things out. I am grateful to any help that is offered to me.
import { useState } from "react";
function FoodPreferences(){
const [formFields, setFormFields] = useState([
{ name: '', age: '', food: '' }
])
const [foodState, setFoodState] = useState("dumpling");
const handleFormChange = (event, index) => {
let data = [...formFields];
data[index][event.target.name] = event.target.value;
setFormFields(data);
}
const handleSelectChange = (event, index) => {
const selectedFood = event.target.value
setFormFields(selectedFood)
}
const submit = (e) => {
e.preventDefault();
console.log(formFields, foodState)
}
const addFields = () => {
let object = {
name: '',
age: '',
food: ''
}
setFormFields([...formFields, object])
}
const removeFields = (index) => {
let data = [...formFields];
data.splice(index, 1)
setFormFields(data)
}
return (
<div className="App">
<form onSubmit={submit}>
{formFields.map((form, index) => {
return (
<div key={index}>
<input
name='name'
placeholder='Name'
onChange={event => handleFormChange(event, index)}
value={form.name}
/>
<input
name='age'
placeholder='Age'
onChange={event => handleFormChange(event, index)}
value={form.age}
/>
<select
className="custom-select"
value={form.food}
onChange={event => handleSelectChange(event,index)}
>
<option value="steak">Steak</option>
<option value="sandwich">Sandwich</option>
<option value="dumpling">Dumpling</option>
</select>
<button onClick={() => removeFields(index)}>Remove</button>
</div>
)
})}
</form>
<button onClick={addFields}>Add More..</button>
<br />
<button onClick={submit}>Submit</button>
</div>
);
}
export default FoodPreferences;
I have tried using the select component alone without looping it and it worked fine. The errors pop up when select component is placed under a map() for dynamic inputs (Adding or Removing Fields). I know that the error is either in the onChange part of my code for the select component or the handleSelectChange
import React, {useState} from 'react';
function FoodChoice() {
const \[foodState, setFoodState\] = useState("dumpling");
return (
<div className="container p-5">
<select
className="custom-select"
value={foodState}
onChange={(e) => {
const selectedFood = e.target.value;
setFoodState(selectedFood);
}}
>
<option value="steak">Steak</option>
<option value="sandwich">Sandwich</option>
<option value="dumpling">Dumpling</option>
</select>
{foodState}
</div>
);
}
export default FoodChoice;
I am working on an application where I am taking data(array of sectors) from firestore database and adding this array to select box options. My problem is that I can't properly display each item in a separate option, instead my entire array is displayed in one line in the select box option.
a screenshot of what my application looks like with the described problem.
Parts of the code responsible for getting an array from the database and displaying it in the select box options:
const [allSectors, setAllSectors] = useState([]);
useEffect(() => {
getSectors();
}, []);
const getSectors = async () => {
const data = await UserDataService.getAllSectorsData();
setAllSectors(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
};
const FormSelect = () => {
return allSectors.map((sector) => (
<option key={sector.id} value={sector.id}>
{sector.Manufacturing}
</option>
));
};
return (
<>
<Form onSubmit={handleSubmit}>
<Form.Group className="mb-3" controlId="formUserSector">
<InputGroup>
<InputGroup.Text id="formUserSector">Sectors: </InputGroup.Text>
<Form.Select size="sm" onChange={(e) => setSectors(e.target.value)}>
{FormSelect()}
</Form.Select>
</InputGroup>
</Form.Group>
</Form>
</>
)
Not sure which UI library you are using. The first thing I noticed is that you use <option> tag without surrounding it with <select> tag
<option key={sector.id} value={sector.id}>
{sector.Manufacturing}
</option>
I'm assuming you are trying to implement HTML <select> dropdown. I think if you use only tags without surrounding it with it will just return strings, which is what happening in your example.
Try changing your FormSelect function to something like this:
const FormSelect = () => {
return (
<select>
{allSectors.map((sector) => (
<option key={sector.id} value={sector.id}>
{item}
</option>
))}
</select>
);
};
EDIT 1
From your comments, I noticed that your sector.Manufacturing is an array, but it should be a string. When you put an array inside option like this <option>{array}</option> your get all the array values squashed into one <option> tag.
try also looping over sector.Manufacturing rather than allSector. Hard to solve without seeing the full code, but it might look something like this:
const FormSelect = () => {
return (
<select>
{allSectors.map((sector) => (
<option key={sector.id} value={sector.id}>
{sector.Manufacturing.map((item) => {
return item;
})}
</option>
))}
</select>
);
};
EDIT 2
Try this:
const FormSelect = () => {
return (
<select>
{allSectors.map((sector) =>
sector.Manufacturing.map((item, i) => {
return (
<option key={i} value={item}>
{item}
</option>
);
})
)}
</select>
);
};
I have simple form with dropdown select menu. How do I reset the select field back to show first option after submission ( ---Select category---)? I have tried setCategory("") or setCategory("---Select category---") but it keeps showing category i have picked.
const [category, setCategory] = useState("");
function formSubmit(e) {
e.preventDefault();
firebase
.firestore()
.collection("posts")
.add({
category,
})
.then(() => {
setCategory("");
});
}
<form onSubmit={formSubmit}>
<div>
<label htmlFor="category">Post Category</label>
<select
name="category"
onChange={(e) => setCategory(e.currentTarget.value)}
>
<option value="">--- Select category ---</option>
<option value="react">React</option>
<option value="CSS">CSS</option>
<option value="misc">Misc</option>
</select>
<div>
<button type="submit">Add</button>
</div>
</div>
After doing a little bit of research I found this questions answered here, here, and here. However, I would advice you to use the react-advanced-form library which makes this task a lot easier and will give you more features to implement on your forms.
While using this library you want to use “form.reset()” on your action button. This will let you set initial values for each field and the action will set the values of your fields to this default value. Here is an example:
import React from 'react'
import { Form } from 'react-advanced-form'
import { Input } from 'react-advanced-form-addons'
export default class Example extends React.Component {
handleSubmitted = ({ res, fields, form }) => {
form.reset() // resets "username" field to "admin"
}
render() {
return (
<Form onSubmitted={this.handleSubmitted}>
<Input
name="username"
initialValue="admin" />
</Form>
)
}
}
I'm trying to use React-final-form with a DropDown in a Child Component.
Can't get this to work.
All of my text fields are already in a Child Component and this works like a charm.
The field in the parent looks like this:
<Field
name="lastName"
placeholder="Last Name"
validate={required}
>
{({input, meta, placeholder}) => (
<MyField meta={meta} input={input} placeholder={placeholder}/>
)}
</Field>
The Child Component looks like this:
export const MyField = (props) => {
return (
<Form.Field className={props.meta.active ? 'active' : ''}>
<Label>{props.label ? props.label : props.placeholder}</Label>
<Form.Input
{...props.input}
placeholder={props.placeholder}
className={(props.meta.error && props.meta.touched ? 'error' : '')}
/>
</Form.Field>
)
};
The "Form.Field" and "Label" are coming from semantic-ui-react
But now I want to do the same with a DropDown.
The standard DropDown, taken from an example on the React-Final-Form site, looks like this:
<Field name="toppingsA" component="select">
<option value="chicken">Chicken</option>
<option value="ham">Ham</option>
<option value="mushrooms">Mushrooms</option>
<option value="cheese">Cheese</option>
<option value="tuna">Tuna</option>
<option value="pineapple">Pineapple</option>
</Field>
And it works in a sense that I'm getting the value in my react-final-form values onSubmit.
then I'm trying to offload the Dropdown itself to the Child Component (with the intention to use the semantic-ui-react version of a Dropdown, but first things first and get the Dropdown to work :-) )
Parent Component:
const eatOptions = [
{key: 'c', text: 'Chicken', value: 'chicken'},
{key: 'h', text: 'Ham', value: 'ham'},
{key: 'm', text: 'Mushrooms', value: 'mushrooms'},
{key: 't', text: 'Tuna', value: 'tuna'}
];
// And in the Form:
<Field name="toppingsB" component="select" options={eatOptions}>
{ ({input, meta, options}) => {
return (
<Opts options={options} name={input.name}/>
)
}}
</Field>
And in the Child Component:
export const Opts = (props) => {
return (
<select name={props.name}>
{props.options.map((x) => {
return (
<option key={x.key} value={x.value}>{x.text}</option>
)
})}
</select>
)
};
Result is that the HTML looks the same (which does not say that much I guess), ToppingsA is picked up in the values (on onSubmit) and ToppingsB is not.
I can't figure out what am I missing here and your help would be very much appreciated.
Thanks in advance,
Bert
If you are using render-props for toppingsB then the Field component prop should not be type "select" as the children of Field will be a function and not multiple tags.
It also looks like you are not letting your form know of any changes that occur inside the child component. Try passing the Opts component an onChange function as a prop:
<Opts
options={options}
name={input.name}
onChange={ (value:string) => input.onChange(value)}
/>
#S.Taylor, Thanks for your help!! It works.
As a reference the working code:
The Field in Parent Component:
<Field name="toppingsB" options={eatOptions} >
{ ({input, meta, options}) => {
return (
<Opts
options={options}
name={input.name}
onChange={ (value) => input.onChange(value)}
/>
)
}}
</Field>
And the code in the Child Component:
export const Opts = (props) => {
return (
<select name={props.name} onChange={props.onChange}>
{props.options.map((x) => {
return (
<option key={x.key} value={x.value}>{x.text}</option>
)
})}
</select>
)
};