React prop only changes state after selecting twice - reactjs

I am using React Select and for some reason my state is only changing after an option has been selected twice.
I have the following code:
var React = require('react');
import Select from 'react-select';
class VehicleSelect extends React.Component {
constructor(props) {
super(props);
this.state = { brandSelect: ""};
}
_onChange(value) {
//console.log(value) - just to see what we recive from <Select />
this.setState({brandSelect: value});
console.log(this.state.brandSelect);
}
render() {
var options = [
{ value: 'Volkswagen', label: 'Volkswagen' },
{ value: 'SEAT', label: 'SEAT' },
{ value: 'SKODA', label: 'SKODA' }
];
return (
<Select
name="form-field-name"
value={this.state.brandSelect}
options={options}
placeholder="Select a brand"
searchable={false}
onChange={this._onChange.bind(this)}
/>
)
}
};
// Export our component
export default VehicleSelect;
When one of the options is selected it won't console.log the new state however if I select the option again it will. Any idea where I am going wrong, I'm guessing because the state isn't being shown in the console.log it isn't updating?
Thanks

setState does not change state immediately. You need to use a callback. Docs.
_onChange(value) {
//console.log(value) - just to see what we recive from <Select />
this.setState({brandSelect: value}, () => {
console.log(this.state.brandSelect);
});
}

Related

How to pre-select some values in react-select?

I am using react-select in a project. When a user clicks to update a post, I fetch the post's original data and fill the form. However I am not able to pre-fill the Select component.
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
tags : [], // this contains the list of all the tags in system
incomingTags : [] // will contain the tags which are linked with the post
selectedTags : [] // contains the tags that have been selected by user
}
}
selectRef = null;
componentDidMount() {
fetch all the tags
this.setState({tags : fetchedTags});
fetch the post data
this.setState({incomingTags : fetchedPostTags});
}
handleTagInputChange = (selectedOptions) => {
this.setState({ selectedTags : selectedOptions });
}
render() {
return (
<CreatableSelect
ref={ref => {this.selectRef = ref;}}
isMulti
options={this.state.tags}
onChange={this.handleTagInputChange}
/>
)
}
}
Things I have tried -
Using defaultValue={this.state.defaultTags} but it does not change when the state is set.
Using value={this.state.defaultTags} but then I am not able to remove the selected tags or select any other tags. I went through a similar query - react-select: pre-select a value in multi-select dropdown
Using this.selectRef.selectOption() but it does not work for selecting multiple items
When you want to use controlled component, you have to set both value and onChange
import React, { Component } from 'react';
import CreatableSelect from 'react-select/creatable';
export default class CreatableSingle extends Component {
constructor(props) {
super(props);
this.state = {
tags : [],
}
}
handleChange = (
newValue
) => {
this.setState({ tags: newValue })
};
render() {
return (
<CreatableSelect
value={this.state.tags}
onChange={this.handleChange}
/>
);
}
}

Using HTML Selectors with React

I'm trying to use the select tag in React. I have the following component:
class InteractionHeader extends React.Component {
constructor(props) {
super(props);
this.state = {
allDays: [],
selected: ""
};
this.dateChanged = this.dateChanged.bind(this);
}
componentDidMount() {
this.setState({
allDays: ["a", "b"],
selected: "a"
});
}
dateChanged(e) {
e.preventDefault();
console.log(event.target.value);
}
}
And in my render, I have the following:
render() {
return (
<div>
<select value={this.state.selected} onChange={this.dateChanged}>
{this.state.allDays.map((x) => {
return <option key={`${x}`} value={`${x}`}>{x}</option>
})};
</select>
</div>
);
}
However, when I select a different option, my console prints undefined instead of the option I selected. What is going on and how do I prevent it?
You are calling it wrong
dateChanged(e) {
console.log(e.target.value);
}
You should use prevent default in case where you donot want your page to take a refresh and try to make a server side call. In case of select there is nothing like this.
You have a typo in dateChanged(e) method. You need to log e.target.value instead of event.target.value.

react material-ui select not working

Im' new to react from angularjs, using material-ui for a project and I can't get the select component to work like a select component. Basically I want to populate the dropdown with an array of objects and do something with the selected object once a selection is made by the user. I've been running into a bunch of problems, the most recent is that I can't figure out how to set a default starting value when the component loads and I can't see the selected option in the GUI. I'm able to set the state and log it out to the console you just can't see it in the select component. Also, what is the difference between #material-ui/core and material-ui. Are they different libraries, different versions of the same library?
class HomePage extends React.Component {
constructor(props) {
super();
this.reportSelected = this.reportSelected.bind(this);
this.state = {
report: "report1"
};
}
static propTypes = {
classes: PropTypes.object
};
reports = [
{
name: "report1"
},
{
name: "report2"
},
{
name: "report3"
}
];
reportSelected = event => {
this.setState((prevState) => {
return {
report: event.target.value
}
}, console.log(this.state))
};
render() {
const { classes, headerTitle } = this.props;
return (
<div className={classes.homePage}>
<HeaderTitle title="Home" />
<Helmet>
<title>{headerTitle}</title>
</Helmet>
<form>
<FormControl className={classes.reportsDropdown}>
<InputLabel htmlFor="reports">Reports</InputLabel>
<Select
value={this.state.report}
onChange={this.reportSelected}
>
{this.reports.map(report => (
<MenuItem value={report.name} key={report.name}>
{report.name}
</MenuItem>
))}
</Select>
</FormControl>
</form>
</div>
);
}
}
UPDATE:
The following code works as expected,
class HomePage extends React.Component {
constructor(props) {
super();
this.reportSelected = this.reportSelected.bind(this);
this.state = {
report: "report1"
};
}
static propTypes = {
classes: PropTypes.object
};
reports = [
{
name: "report1"
},
{
name: "report2"
},
{
name: "report3"
}
];
reportSelected = event => {
this.setState(() => {
return {
report: event.target.value
}
})
};
render() {
const { classes, headerTitle } = this.props;
return (
<div className={classes.homePage}>
<HeaderTitle title="Home" />
<Helmet>
<title>{headerTitle}</title>
</Helmet>
<form>
<FormControl className={classes.reportsDropdown}>
<InputLabel htmlFor="reports">Reports</InputLabel>
<Select
value={this.state.report}
onChange={this.reportSelected}
>
{this.reports.map(report => (
<MenuItem value={report.name} key={report.name}>
{report.name}
</MenuItem>
))}
</Select>
</FormControl>
</form>
</div>
);
}
}
I would imagine the problem is that the initial selected value must match a value of an item in the select.
In the code sample you are using the name property this.reports[0].name as the initial value, but your menu items use the object itself for the value, i.e. value={report}.
Either use the name property for the value of the menu items or use this.reports[0] as your initial value and see if that works.
As for your second question, material-ui is the previous version of the library (the 0.xx series). #material-ui is the latest and greatest 1.11 version.

Unable to pass label in react-select dropdown

I am not able to pass the label value in onChange event handler. Only the value is getting formed. I want to have access to the label. Do help with the same.
handleSelectChange function has access only to the value and not to the label or event of the select dropdown.
<Select name={this.props.name}
backspaceToRemoveMessage=""
closeOnSelect={!this.state.stayOpen}
searchable={false}
disabled={this.state.disabled}
multi
options={this.props.countries}
simpleValue={true}
value={this.state.value}
onChange ={this.handleSelectChange}
/>
It is because flag simpleValue=true parse handler parameter to string of values. Set this flag to false and your handleSelectChange will have access to array of selected objects. Than you have to generate your value manually. (e.g. value="one,two")
Example of value processing:
handleSelectChange = (val) => {
const value = val.map(item => item.value);
this.setState({
value
});
}
You can grab element that is selected in the example below in the updateState.
Example
import React from "react";
import Select from 'react-select';
export default class CoolExample extends React.Component {
constructor(props) {
super(props);
this.state = {value:"foo"};
}
updateState(element) {
this.setState({value: element});
}
render(){
var Select = require('react-select');
var options = [
{ value: 'foo', label: 'Foo' },
{ value: 'bar', label: 'Bar' }
];
return(
<Select
name="form-field-name"
value={this.state.value}
options={options}
onChange={this.updateState.bind(this)}
/>
);
}
}

Formsy-material-ui do not validate initial render

Is there any way, one can delay first validation of components in formsy-material-ui so that validations like isNotEmpty do not fire on first render of the form and mess the UX? I am using controlled components, therefore setting value from state on each render.
<FormsyText
name="name"
value={this.state.name}
floatingLabelText="Name"
onChange={partial(this._changeInputValue, ['name'])}
validations={{ isNotEmpty }}
validationError="Field shoud not be empty"
/>
I needed this solution too. I've been looking into the source code of formsy-material-ui, and it seems that the text field is setting its value right before it's mounted. That's why the field is marked changed (aka not pristine) when the rendering happens, so the validation error is shown.
Anyways, I wrote a hackish solution using a higher order component. I've been testing with text fields only, but should work with any fields having this problem. The core concept: if the formsy field doesn't have a "validationErrors" prop, it's not showing any errors.
import React, { Component, PropTypes } from 'react';
export const preventFirstValidation = (FormsyField) => {
return class extends Component {
static propTypes = { preventFirstValidation: PropTypes.bool };
static defaultProps = { preventFirstValidation: true };
constructor(props) {
super(props);
this.state = { isChanged: false };
}
render() {
const { preventFirstValidation, ...fieldProps } = this.props;
return (
<FormsyField
{...fieldProps}
onChange={(evt, val) => {
if (!this.state.isChanged) this.setState({ isChanged: true });
if (this.props.onChange) this.props.onChange(evt, val);
}}
validationErrors={(this.state.isChanged || !preventFirstValidation) ? this.props.validationErrors : undefined}
/>
);
}
};
};
How to use it:
import { Form } from 'formsy-react';
import FormsyTextField from 'formsy-material-ui/lib/FormsyText';
const TextField = preventFirstValidation(FormsyTextField);
const MyForm = () => (
<Form>
{/* You're using the transformed field, exactly like before */}
<TextField
name = "some_field"
validationErrors={{ isRequired: 'This is really required!' }}
required
preventFirstValidation={ /* you can enable or disable this feature */ }
/>
</Form>
);

Resources