What I have here is a function where I call the codigo, and the nombre, in the DB
table registrations. What I want to achieve is that the digital code that is like an autocomplete to fill in the name when you select the code.
enter image description here
class Matriculas extends Component {
state = {
status: "initial",
data: []
}
componentDidMount = () => {
this. getInfo()
}
getInfo= async () => {
try {
const response = await getAll('matriculas')
console.log(response.data)
this.setState({
status: "done",
data: response.data
});
} catch (error) {
this.setState({
status: "error"
});
}
};
render() {
const data = [...this.state.data];
return (
<Container>
<RowContainer margin="1px" >
<ColumnContainer margin="10px">
<h3>Info</h3>
<label>Codigo</label>
<Input
width='150px'
type="text"
placeholder="Digite el codigo"
value={data.codigo } ref="codigo" />
<label>Nombre</label>
<Input
width='150px'
type="text"
placeholder="Nombre completo"
value={data.nombre} />
</ColumnContainer>
</RowContainer>
</Container>
)
}
};
export default Matriculas;
What you most likely want to use is react-select
You can pass options to the select (which would be your names) and it will return values that match whatever you type in the search bar.
import Select from 'react-select'
const options = [
{ value: 'mike', label: 'Mike' },
{ value: 'john', label: 'John' },
{ value: 'vanessa', label: 'Vanessa' }
]
const MyComponent = () => (
<Select options={options} />
)
So you can take that example, and the examples in the link, and put it in your code:
import Select from 'react-select'
<Container>
<RowContainer margin="1px" >
<ColumnContainer margin="10px">
<h3>Info</h3>
<label>Codigo</label>
<Input
width='150px'
type="text"
placeholder="Digite el codigo"
value={data.codigo } ref="codigo" />
<label>Nombre</label>
<Select
value={this.state.nameValue}
onChange={event => {this.setState({nameValue: e.value})}
options={options} />
</ColumnContainer>
</RowContainer>
</Container>
When using onChage, it returns an event, which has the value of the selected name. You can use that to set the state's nameValue, and then use that name value in the rest of your component as well
Once you get this up and running, it also worth looking at the async select, which allows you to give an async function that returns values (your getInfo function, for example)
-- edit --
If you want to define the onChange event elsewhere, it would look like this:
handleChange = event => {
// event.value will be the value of the select
this.setState({optionSelected: event.value});
}
and then in your onChange, tell it that is the function you want but do not invoke it (don't write it with parentheses):
<Select
value={this.state.optionSelected}
onChange={this.handleChange}
options={options} />
Related
I am sending my state as a stringified variable from a form to a POST request through a lamda server which then parses it and sends it to sendgrid which there I am using send grids templating feature. Which requires me to have json Formatted like this in order to loop over one particular part (multiple checkboxes) which all should have the same key but a different value, set by a reason="Weight Loss" in the form. Here is how the eventual json needs to be formed.
{
"name" :"anders",
"message" : "winfsdafasfdsfsadfsadnipeg",
"package" : "silver",
"email" : "email#email.com",
"subject" : "fdsafas",
"data":{
"reasonArray":[
{
"reason":"weightLoss"
},
{
"reason":"Sport"
}
]
}
}
Then I can do some magic and loop over the reason's that were checked in the checkbox
<ol>
{{#each data.reasonArray}}
<li>{{this.reason}} </li>
{{/each}}
</ol>
Now I had it working if I left the state with single key value pairs and don't have the data portion.
Here is what my initial state looked like working.
const [formState, setFormState] = React.useState({
name: "",
package: `${data.datoCmsPricing.title}`,
email: "",
subject: "",
weightLoss:"",
strength:"",
sport:"",
message: "",
})
I then had the following onChange event that set the state with the name of the input field as the key and the value or checked state as the value. Seen here
const onChange = (e) => {
if (e.target.type === 'checkbox' && !e.target.checked) {
setFormState({...formState, [e.target.name]: e.target.checked});
} else {
setFormState({...formState, [e.target.name]: e.target.value });
}
}
and here is my form
<form onSubmit={submitForm}>
{/* <input type="text" name="package" value={data.datoCmsPricing.title} /> */}
<label>
Name
<input
type="text"
name="name"
value={formState.name}
onChange={onChange}
/>
</label>
<label>
Email
<input
type="email"
name="email"
value={formState.email}
onChange={onChange}
/>
</label>
<label>
Subject
<input
type="text"
name="subject"
value={formState.subject}
onChange={onChange}
/>
</label>
<div>
<h3>Reasons for wanting to train</h3>
<label>
Weight Loss
<input
type="checkbox"
name="weightLoss"
checked={formState.weightLoss}
onChange={onChange}
/>
</label>
<label>
Strength
<input
type="checkbox"
name="strength"
checked={formState.strength}
onChange={onChange}
/>
</label>
<label>
Sport
<input
type="checkbox"
name="sport"
checked={formState.sport}
onChange={onChange}
/>
</label>
</div>
<label>
message
<textarea
name="message"
value={formState.message}
onChange={onChange}
/>
</label>
<button type="submit">Submit</button>
</form>
I then send it off to my lamdba function
const response = await fetch("/.netlify/functions/sendmail", {
method: "POST",
body: JSON.stringify(formState),
})
Now my state looks like the following in json after being sent to lamdbda function and being parsed
{
name: 'Anders',
package: 'silver',
email: 'email#email.com',
subject: 'fdsafa',
weightLoss: 'on',
strength: 'on',
sport: 'on',
message: 'fdsafasf'
}
Now I want to have my initial state to look like the format that sendgird wants it in, so this is what I attempted with my state setup.
const [formState, setFormState] = React.useState({
name: "",
package: `${data.datoCmsPricing.title}`,
email: "",
subject: "",
weightLoss:"",
strength:"",
sport:"",
message: "",
data:{
reasonArray:[
{
reason:""
},
{
reason:""
}
]
}
})
Then I tried to update the onChange event for the checked values with the following, I also update my form so it grabs a user friendly name for the reason. Seen below this code
const onChange = (e) => {
if (e.target.type === 'checkbox' && !e.target.checked) {
setFormState({...formState, data:{ reasonArray:[ { reason:e.target.reason}, ]}});
}
...
}
Form Changes
...
<label>
Weight Loss
<input
type="checkbox"
name="weightLoss"
reason="weightLoss"
checked={formState.weightLoss}
onChange={onChange}
/>
</label>
<label>
Strength
<input
type="checkbox"
name="strength"
reason="strength"
checked={formState.strength}
onChange={onChange}
/>
</label>
<label>
Sport
<input
type="checkbox"
name="sport"
reason="sport"
checked={formState.sport}
onChange={onChange}
/>
</label>
...
The resulting Json I get after the Post request is this, with my attempt. It does not update the data part. So the resulting Json is in the right format, but it doesn't have the reason's attached. Thanks ahead of time for any help.
{
"name":"Anders",
"package":"Silver",
"email":"email#email.com",
"subject":"fdsaf",
"weightLoss":"on",
"strength":"on",
"sport":"on",
"message":"fdsafas",
"data":{
"reasonArray":[
{
"reason":""
},
{
"reason":""
}
]
}
}
Attempting Rabi's answer
...
const prepareDataForApi = (formData) => {
const newFormData = Object.assign({}, formData); // optional if passed cloned copy of formData object or you can also use lodash cloneDeep
newFormData.data = {
reasonArray:[]
};
Object.keys(newFormData.reasons).forEach(key => {
if(newFormData.reasons[key]){
newFormData.data.reasonArray.push({reason: key})
}
});
delete newFormData.reasons;
return newFormData;
}
const submitForm = async (e) => {
e.preventDefault();
setForm(false);
// const newFormData = prepareDataForApi(formData);
const newFormData = prepareDataForApi(formState);
console.log(newFormData);
...
1.Keep your initial state like this :
{
"name":"Anders",
"package":"Silver",
"email":"email#email.com",
"subject":"fdsaf",
"message":"fdsafas",
"reasons": {
"weightLoss": true,
"strength": true,
"sport": true,
}
}
Modify onChange():
const onChange = (e) => {
if (e.target.type === 'checkbox') {
const changedReason = e.target.getAttribute('name');
setFormState({...formState, reasons:{...formState.reasons, [changedReason]: !formState.reasons[changedReason]}});
}
...
}
Change form's onSubmit():
Before calling api , call converter function which will convert formState to JSON format required by your lambda function
const prepareDataForApi = (formData) => {
const newFormData = Object.assign({}, formData); // optional if passed cloned copy of formData object or you can also use lodash cloneDeep
newFormData.data = {
reasonArray:[]
};
Object.keys(newFormData.reasons).forEach(key => {
if(newFormData.reasons[key]){
newFormData.data.reasonArray.push({reason: key})
}
});
delete newFormData.reasons;
return newFormData;
}
It looks like your new onChange is not replacing all of your nested values when you update a key. Try this instead:
setFormState({
...formState,
data:{
...formState.data, // keep keys from previous data object (not necessary if it only contains the key you are specifying though)
reasonArray:[
...formState.data.reasonArray, // keep previous entries from reasonArray
{ reason:e.target.reason},
]
}
});
An alternative would be to use an effect.
const [formState, setFormState] = React.useState({...}):
// Runs every time weightLoss is changed
React.useEffect(() => {
let newReasonArray
if (formState.weightLoss) {
newReasonArray = [...formState.reasonArray]; // So we dont mutate state
newReasonArray.push({reason: 'weightLoss'});
} else {
// If you want to remove it from the array if its unchecked
newReasonArray = [...formState.reasonArray];
newReasonArray.filter((reason) => (reason.reason != 'weightLoss'));
}
console.log(newReasonArray) // Test if it is updated correctly
// Set the state with the new array
setFormState({...formState, data: { reasonArray: newReasonArray }});
}, [formState.weightLoss]);
I try to fill in a dropdown with data from the JSON format but for now the dropdown is empty (no results found...)
I certainly have a mistake and I can not understand where I'm confusing.
I will attach a screen of my API.
I want to get Station and NameStation..
API for Stations
My code:
import React, { Component } from 'react';
import Select from 'react-select';
import 'react-select/dist/react-select.css';
function parseStations(stations){
return stations.map((station) => {
return { label: station.NameStation, value: station.Station };
});
}
export default class Weather extends Component {
constructor(props) {
super(props);
this.state = {
options: [
{ value: true, label: 'Yes' },
{ value: false, label: 'No' }
], stations: [
],
value: null
}
this.onChange = this.onChange.bind(this);
}
onChange(event) {
this.setState({ value: event.value });
console.log('Boolean Select value changed to', event.value);
}
componentDidMount() {
this.getStations();
}
getStations() {
fetch('http://localhost:56348/api/stations', {
data: 'Station',
data: 'NameStation',
method: "GET"
}).then(res => res.json())
.then(res => this.setState({ stations: parseStations(res.stations) }))
//.then(res => this.setState({ stations: res.stations }))
//.catch(e => )
}
render() {
return (
<div className="MasterSection">
<div className="wrapper">
<div className="section">Изберете № на станция</div>
<Select
onChange={this.onChange}
//options={this.state.options}
options={this.state.stations}
value={this.state.value}
clearable={false}
/>
</div>
<div class="section">
<input type="text" class="form-control" placeholder="Брой дни назад" aria-label="Username" aria-describedby="basic-addon1"></input>
</div>
<div class="section">
<button type="button" class="btn btn-outline-dark">Покажи</button>
</div>
</div>
);
}
}
Seems you made a typo naming the prop stations instead of options :
<Select
onChange={this.onChange}
options={this.state.stations} // here
value={this.state.value}
clearable={false}
/>
Edit : you'll need to parse your json first to pass a proper array of objects like this : [{ label: nameStation, value: Station }]
Edit 2 : Here's a parser for your data :
function parseStations(stations){
return stations.map((station) => {
return { label: station.NameStation, value: station.Station };
});
}
You can call this in your async request before setting the state :
.then(res => this.setState({ stations: parseStations(res.stations) }))
componentDidMount() is executed only after render() is completed. so there's no way getStations() gets executed at the time your UI gets rendered. it is not a good idea to setState inside componentDidMount() as it triggers re rendering. use componentWillMount() instead.
correct the typo that Dyo mentioned and use options={this.state.stations}
Can someone help me out how to use onChange in dropdown (Semantic UI React). I am reading the docs, but still, I don't get it. It does have onChange props.
onChange(event: SyntheticEvent, data: object)
How do I use it? Like, I have method dropdownmethod().
edit - implemented the suggestion, but it didn't work. I think in your suggestion, you didn't bind the function. But, I bind the function.
onChangeFollower(event,data){
console.log("on change follower",data.text)
}
render() {
console.log("this.props",this.props)
var onChangeFollower = this.onChangeFollower.bind(this)
return (
<div>
<h2>project settings are here</h2>
<h2>Add new Member</h2>
<Dropdown onChange={onChangeFollower}
placeholder='Select Member'
fluid search selection options={arr} />
<h2>List of members</h2>
{lr}
</div>
As stated in the docs, you just need to pass a reference of your method and then you will get 2 parameters:
The native event
The object of the option selected
Here is a running example
Here is a code snippet (it uses a CDN but throws some debug warnings, so ignore them)
const { Dropdown } = semanticUIReact;
const languageOptions = [
{ key: 'eng', text: 'English', value: 'eng' },
{ key: 'spn', text: 'Spanish', value: 'spn' },
{ key: 'rus', text: 'Russian', value: 'Russian' },
]
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
searchQuery: '',
selected: null
}
}
onChange = (e, data) => {
console.log(data.value);
this.setState({ selected: data.value });
}
onSearchChange = (e, data) => {
console.log(data.searchQuery);
this.setState({ searchQuery: data.searchQuery });
}
render() {
const { searchQuery, selected } = this.state;
return (
<div>
<Dropdown
button
className='icon'
fluid
labeled
icon='world'
options={languageOptions}
search
text={searchQuery}
searchQuery={searchQuery}
value={selected}
onChange={this.onChange}
onSearchChange={this.onSearchChange}
/>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.9/semantic.min.css"/>
<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>
<script src="https://cdn.jsdelivr.net/npm/semantic-ui-react#0.77.1/dist/umd/semantic-ui-react.min.js"></script>
<div id="root"></div>
Edit
As a followup to your comment.
I checked example. Even there when i type something, it doesnt show me
anything in console
You are not talking about onChange of the select you are talking about when the search input has changed.
You can use onSearchChange with same parameters. (I've updated the example)
I have implemented as below
React hooks function is as below , if you prefer you can pass handleOnChange as a prop as well
const RenderDropdown = ({optionsArray}) => {
const handleOnChange = (e, data) => {
console.log(data.value);
}
return (
<Dropdown
placeholder='Please select'
fluid
selection
options={optionsArray}
onChange={handleOnChange}
/>
);
}
options array as below
const optionsArray = [
{
key: 'male',
text: 'Male',
value: 'male',
image: { avatar: true, src: 'https://semantic-ui.com/images/avatar/small/elliot.jpg' },
},
{
key: 'female',
text: 'Female',
value: 'female',
image: { avatar: true, src: 'https://semantic-ui.com/images/avatar/small/stevie.jpg' },
}
]
use onChange event to detect the changes in the dropdown list
onSearchChange={(e, v) => {
changeMethod(e, v)
}}
use onSearchChange event to detect the search input
onSearchChange={(e, v) => {
searchMethod(e, v)
}}
and you have to define searchMethod and changeMethod as consts in the top of your page.
Below is the working code of your's:
onChangeFollower(event, data){
console.log("on change follower",data.text)
}
render() {
console.log("this.props",this.props)
return (
<div>
<h2>project settings are here</h2>
<h2>Add new Member</h2>
<Dropdown onChange={this.onChangeFollower}
placeholder='Select Member'
fluid search selection options={arr} />
<h2>List of members</h2>
{lr}
</div>
)
}
this might be a dumb question, but I'm new to react and I'm having some difficulty with my drop-down menu. Long story short, I've got a list of locations, and I need to be able to 'pull out' whichever location the user picks from the menu (I plan on using that location info to generate a map using Google's static maps API).
<form onSubmit={this.handleSubmit}>
<select className="menu" name="select" value={this.state.value} onChange={this.searchLocations}>
{options}
</select>
<input type="submit" value="Submit" />
</form>
(and this is the code that generates {options}):
let options = this.state.locationsBase.map((item, index) => {
return(
<option key={index + 1} value={this.state.locationsBase}>{item.name}</option>
)
})
locationsBase is an array that's loaded in with componentDidMount.
So my problem is this:
<select value= > is returning 'undefined', when I need it to return whichever location the user clicks on (and the values for locations are contained in {options} ). (also, these locations all show up in the drop-down menu).
I don't know if this is very clear, but any help would be greatly appreciated. Thanks.
update
this is what the searchLocations function looks like:
searchLocations(e){
e.preventDefault()
let selectedLocation = this.locationQuery
console.log(selectedLocation)
this.setState({
locationResults: this.state.selectedLocation
})
selectedLocation returns undefined in the console.
and locationQuery is set to this.value in my initial state.
update (full component):
class LocationsContainer extends Component {
constructor(props){
super(props)
this.state = {
locationQuery: this.value,
locationResults: "",
locationsBase: []
}
this.handleOnChange = this.handleOnChange.bind(this)
this.searchLocations = this.searchLocations.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
componentDidMount(){
this.setState({
locationsBase:[
{name: "Pick a branch" , address: ""},
{name: "Anacostia" , address: "1800 Good Hope Rd SE"},
{name: "Bellevue" , address: "115 Atlantic St SW"},
{name: "Benning" , address: "3935 Benning Rd NE"},
{name: "Capitol View" , address: "5001 Central Ave SE"},
]
}, () => {
console.log(this.state.locationsBase)
})
}
handleOnChange(e) {
const name = e.target.value
this.setState({
[name]: e.target.value
})
console.log("name")
}
searchLocations(e){
e.preventDefault()
let selectedLocation = this.locationQuery
console.log(selectedLocation)
this.setState({
locationResults: this.state.selectedLocation
})
console.log(this.locationResults, 'something is working')
}
handleSubmit(e) {
alert('Your location is: ' + this.state.value);
event.preventDefault();
}
render (){
let options = this.state.locationsBase.map((item, index) => {
return(
<option key={index + 1} value={this.state.locationsBase}>
{item.name}</option>
)
})
return (
<div>
<h3>Choose your branch!</h3>
<form onSubmit={this.handleSubmit}>
<select className="menu" name="select" value={this.state.value}
onChange={this.searchLocations}>
{options}
</select>
<input type="submit" value="Submit" />
</form>
<p>{this.state.locationResults}</p>
</div>
)
}
}
export default LocationsContainer
It looks like you need to pass the onChange event to the searchLocations function.
<select onChange={( e ) => this.searchLocations( e ) }>
{options}
</select>
Then grab the value at e.target.value and pass that to your this.locationQuery function.
Hope this helps!
The example code in the react-bootstrap site shows the following. I need to drive the options using an array, but I'm having trouble finding examples that will compile.
<Input type="select" label="Multiple Select" multiple>
<option value="select">select (multiple)</option>
<option value="other">...</option>
</Input>
You can start with these two functions. The first will create your select options dynamically based on the props passed to the page. If they are mapped to the state then the select will recreate itself.
createSelectItems() {
let items = [];
for (let i = 0; i <= this.props.maxValue; i++) {
items.push(<option key={i} value={i}>{i}</option>);
//here I will be creating my options dynamically based on
//what props are currently passed to the parent component
}
return items;
}
onDropdownSelected(e) {
console.log("THE VAL", e.target.value);
//here you will see the current selected value of the select input
}
Then you will have this block of code inside render. You will pass a function reference to the onChange prop and everytime onChange is called the selected object will bind with that function automatically. And instead of manually writing your options you will just call the createSelectItems() function which will build and return your options based on some constraints (which can change).
<Input type="select" onChange={this.onDropdownSelected} label="Multiple Select" multiple>
{this.createSelectItems()}
</Input>
My working example
this.countryData = [
{ value: 'USA', name: 'USA' },
{ value: 'CANADA', name: 'CANADA' }
];
<select name="country" value={this.state.data.country}>
{this.countryData.map((e, key) => {
return <option key={key} value={e.value}>{e.name}</option>;
})}
</select>
bind dynamic drop using arrow function.
class BindDropDown extends React.Component {
constructor(props) {
super(props);
this.state = {
values: [
{ name: 'One', id: 1 },
{ name: 'Two', id: 2 },
{ name: 'Three', id: 3 },
{ name: 'four', id: 4 }
]
};
}
render() {
let optionTemplate = this.state.values.map(v => (
<option value={v.id}>{v.name}</option>
));
return (
<label>
Pick your favorite Number:
<select value={this.state.value} onChange={this.handleChange}>
{optionTemplate}
</select>
</label>
);
}
}
ReactDOM.render(
<BindDropDown />,
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>
<div id="root">
<!-- This element's contents will be replaced with your component. -->
</div>
// on component load, load this list of values
// or we can get this details from api call also
const animalsList = [
{
id: 1,
value: 'Tiger'
}, {
id: 2,
value: 'Lion'
}, {
id: 3,
value: 'Dog'
}, {
id: 4,
value: 'Cat'
}
];
// generage select dropdown option list dynamically
function Options({ options }) {
return (
options.map(option =>
<option key={option.id} value={option.value}>
{option.value}
</option>)
);
}
<select
name="animal"
className="form-control">
<Options options={animalsList} />
</select>
Basically all you need to do, is to map array. This will return a list of <option> elements, which you can place inside form to render.
array.map((element, index) => <option key={index}>{element}</option>)
Complete function component, that renders <option>s from array saved in component's state. Multiple property let's you CTRL-click many elements to select. Remove it, if you want dropdown menu.
import React, { useState } from "react";
const ExampleComponent = () => {
const [options, setOptions] = useState(["option 1", "option 2", "option 3"]);
return (
<form>
<select multiple>
{ options.map((element, index) => <option key={index}>{element}</option>) }
</select>
<button>Add</button>
</form>
);
}
component with multiple select
Working example: https://codesandbox.io/s/blue-moon-rt6k6?file=/src/App.js
A 1 liner would be:
import * as YourTypes from 'Constants/YourTypes';
....
<Input ...>
{Object.keys(YourTypes).map((t,i) => <option key={i} value={t}>{t}</option>)}
</Input>
Assuming you store the list constants in a separate file (and you should, unless they're downloaded from a web service):
# YourTypes.js
export const MY_TYPE_1="My Type 1"
....
You need to add key for mapping otherwise it throws warning because each props should have a unique key. Code revised below:
let optionTemplate = this.state.values.map(
(v, index) => (<option key={index} value={v.id}>{v.name}</option>)
);
You can create dynamic select options by map()
Example code
return (
<select className="form-control"
value={this.state.value}
onChange={event => this.setState({selectedMsgTemplate: event.target.value})}>
{
templates.map(msgTemplate => {
return (
<option key={msgTemplate.id} value={msgTemplate.text}>
Select one...
</option>
)
})
}
</select>
)
</label>
);
I was able to do this using Typeahead. It looks bit lengthy for a simple scenario but I'm posting this as it will be helpful for someone.
First I have created a component so that it is reusable.
interface DynamicSelectProps {
readonly id: string
readonly options: any[]
readonly defaultValue: string | null
readonly disabled: boolean
onSelectItem(item: any): any
children?:React.ReactNode
}
export default function DynamicSelect({id, options, defaultValue, onSelectItem, disabled}: DynamicSelectProps) {
const [selection, setSelection] = useState<any[]>([]);
return <>
<Typeahead
labelKey={option => `${option.key}`}
id={id}
onChange={selected => {
setSelection(selected)
onSelectItem(selected)
}}
options={options}
defaultInputValue={defaultValue || ""}
placeholder="Search"
selected={selection}
disabled={disabled}
/>
</>
}
Callback function
function onSelection(selection: any) {
console.log(selection)
//handle selection
}
Usage
<div className="form-group">
<DynamicSelect
options={array.map(item => <option key={item} value={item}>{item}</option>)}
id="search-typeahead"
defaultValue={<default-value>}
disabled={false}
onSelectItem={onSelection}>
</DynamicSelect>
</div>