Unexpected behavior with material ui Select and 'redux-form' - javascript

I have a simple 'redux form' with a Select component from newest material-ui-next.
import { TextField } from 'material-ui';
<Field
name="name"
component={TextField}
select
>
<MenuItem value={1}>Lily</MenuItem>
<MenuItem value={2}>Mark</MenuItem>
</Field>
Works fine. Hovewer, if I change the value prop from typeof number to string, e.g.
<Field
name="name"
component={TextField}
select
>
<MenuItem value="lily">Lily</MenuItem>
<MenuItem value="mark">Mark</MenuItem>
</Field>
the value changes properly, but just after one second, the value becomes 0 (as it was initially), and the selected value disappears (it's empty from now on). It had a correct value just for a moment, but somehow it's being automatically set back to 0.
Even tried with rendering the field:
const renderSelectField = ({ input, label, meta: { touched, error }, children, ...custom }) => (
<TextField
{...input}
select
onChange={(event, index, value) => input.onChange(event.target.value)}
children={children}
{...custom}
/>
)
Still, it changes the value, and just after that it returns to 0. If I console.log the form values, it shows up (after manually changing the value):
{ name: "Lily" }
{ name: 0 }
{ name: 0 }
(it happens in period of one second)
Looking forward for any help. Thank you.
Edit: This is what happens in redux dev tools, when choosing an item with string value - in this case pln.

Based on this react-select issue and this redux-form issue it seems like you need to override the default onBlur event.

Related

React final form, populate input from API, but still allow user to override it

I have a input in react final field. The user can input their own value. Theres an option to do something in the form, which makes an api call, and stores the data in state. If that is an option, we populate the input with that value.
However I want to user to be able to override that value. If the API call is made again, I want the value to replace the current input, but still be able to be overiden.
I can't fully delete the value from the api, it won't let me delete all of it and override the data.
File where I render the input, and state is handled.
function RenderFilter(index: number) {
const apiValue = dataFromAPI;
return <TextBox key={index} value1={apiValue ? apiValue.value : null} />;
}
TextBox.jsx
import { Field } from 'react-final-form';
export interface IInput {
name: string;
value1: string;
}
function TextBox({ name, value1 }: IInput) {
return (
<Field name={name}>
{({ input }) => (
<>
<input
name={input.name}
value={input.value}
onChange={input.onChange}
/>
{value1 && input.onChange(value1)}
</>
)}
</Field>
);
}
export { TextBox };
Any ideas?

ReactSelect - closeOnMenuSelect closes the menu on select even when set to false

I have a react-select component field. I have provided react-select with the following props closeOnMenuSelect={false} && isMulti. Which, theoretically should make the select component not close the menu on selecting an item, but for some reason it does close.
Very weird thing is, everywhere else, I have used that same config for the select component I get it working just fine.
Here is the react-select config:
<Field
name={`${keyField}.${index}.permissions`}
render={({ field: { value, name }, form: { setFieldValue, setFieldTouched } }) => (
<div>
<label htmlFor="namespace-permissions">
Permissions in Namespace <span className="text-danger">*</span>
</label>
<Select
isMulti
closeMenuOnSelect={false}
id="namespace-permissions"
defaultValue={convertNamespaceToDefaultValue(
dependencies.namespacePermissions,
value
)}
options={convertNamespaceToSelect(dependencies.namespacePermissions)}
onChangeCallback={values => {
setFieldValue(name, convertSelectToNamespacesData(values));
setFieldTouched(name, true);
}}
/>
<ErrorMessage name={name} component={FormErrorMessage} />
</div>
)}
/>
Why is it NOT working? And why is the exact same config on another react-select works perfectly without a hitch?
I have update the Description, to show that the react-select is wrapped in a Formik Field. As I said, this is a technique I have used in other parts of my code, but this one, isn't working.

Creating a label alongside an input using material-ui's TextField and getInputProps

I am using a material-ui TextField to create an input and a label for a typeahead picker type component with downshift.
I've seen the demos, and have got this:
<FormControl fullWidth className={classname}>
<TextField fullWidth InputProps={ inputProps } InputLabelProps={ inputLabelProps } />
</FormControl>
My inputProps are equal to:
const inputProps = getInputProps({ //passed in by {...downshiftProps} on the parent
onChange, onKeyDown, disabled, error, label, value: inputValue,
startAdornment: selectedItems.map(item => ...)
});
const inputLabelProps = getLabelProps({ //passed in by {...downshiftProps} on the parent
disabled, required, error, disabled, shrink: true, id: inputProps.id
});
My predecessor had those inputLabelProps attributes defined on a material-ui InputLabel component, but in an attempt to get the aria-labelledby attributes working, I've got with a single TextField.
Printing out the content of the input and label props gives:
//labelProps
{
disabled: false,
error: false,
htmlFor: "downshift-0-input",
id: "downshift-0-label",
required: undefined,
shrink: false
}
//inputProps
{
aria-activedecendant: null,
aria-autocomplete: "list"
aria-controls: null,
aria-labelledby: "downshift-0-label",
autoComplete: "off"
disabled: false,
error: false,
id: "downshift-0-input",
onBlur: f(event),
onChange: f(event),
onKeyDown: f(event),
startAdornment: [],
value: ""
}
My issue is that no label is rendered to the DOM. The closest I get is a div with a label attribute, that wraps the input, but displays nothing.
p.s. I've seen Material-ui textfield with label but I don't think FloatingLAbelText exists anymore? The link in the answers is out of date, and not compatible with the propGetters pattern.
Here is a slightly simplified version of the demo mentioned in the comments:
The DownshiftMultiple portion of the demo used a label. I've only included the first downshift demo in my sandbox, but modified it to use a label in the same manner as the "multiple" demo. The label doesn't belong in the InputLabelProps, it is just a property of TextField. In the demo, this is a little confusing to follow because label is specified as a property on the object passed to renderInput so it just ends up as part of the ...other object that is the spread onto TextField.
If you look at the relevant code for TextField you'll see:
{label && (
<InputLabel htmlFor={id} ref={this.labelRef} {...InputLabelProps}>
{label}
</InputLabel>
)}
Here you see that the InputLabelProps don't impact the content of the label, just the other properties that affect its rendering, so you need label as a property directly on TextField.

React-Bootstrap set value of FormControl Select

I have a FormControl which is a Select that I am using in a form. It works fine when adding a new resource, but when I want to edit an existing record I need to set the value of the control. I'm not seeing a good way to set the value of the select / combobox. This is what my JSX looks like.
<FormGroup controlId="group">
<ControlLabel>Group</ControlLabel>
<FormControl componentClass="select" placeholder="Group"
inputRef={ref => { this.groupSelect = ref; }}
onChange={this.groupSelect}>
<option></option>
{
this.state.groups.map(function (group) {
return <option key={group.id} value={group.id}>{group.name}</option>
})
}
</FormControl>
</FormGroup>
I've tried to access the component this way but it comes up undefined.
console.debug(this.groupSelect);
this.groupSelect.value(1);
UPDATE:
I'm trying the following, but this doesn't appear to be working either because I don't have access in the state, calling bind causes an error.
<FormGroup controlId="group">
<ControlLabel>Group</ControlLabel>
<FormControl componentClass="select" placeholder="Group"
inputRef={(ref) => { this.state.groupSelect = ref }}
onChange={this.groupSelect}>
<option></option>
{
this.state.groups.map(function (group) {
return <option key={group.id} value={group.id} selected={this.state.selectedGroupId == group.id}>{group.name}</option>
}).bind(this)
}
</FormControl>
</FormGroup>
You are missing a crucial piece here, which is you need to set the value of the component. Add:
value={this.state.groupSelectValue || defaultValue}
Where groupSelectValue is the value from the record you're editing, and the defaultValue is what you want to default to if there is no record or no value in the record.
Then, in the onChange event function (groupSelect(e)), you will do:
this.setState({
groupSelectValue: e.target.value
});
So that the state is updated with the selection.
The selected value should come from model (state or property), you are trying to achieve what you need with 'jquery way '- which is wrong.

Specify default value to FormControl of react-bootstrap

In react-bootstrap#0.24.5 I've used Input attribute defaultValue to specify start value selected in combobox
<Input type='select'
ref='templateSelect'
defaultValue={this.state.templateId}
onChange={this.handleTemplateChange}>
{options}
</Input>
How this should be handled in react-bootstrap#0.30.7 ( newest one ) where Input was deprecated and new component that should be used here FormControl doesn't provide such attribute?
Should value be used instead?
<FormControl type='select'
ref='templateSelect'
value={this.state.templateId}
onChange={this.handleTemplateChange}>
{options}
</FormControl>
Or maybe something like this:
value={this.state.templateId || 'default value'}
I didn't test this, but from the React Bootstrap source code for FormControl it seems like using defaultValue prop should work:
<FormControl type="select"
ref="templateSelect"
defaultValue={this.state.templateId}
onChange={this.handleTemplateChange}>
{options}
</FormControl>
If multi select defaultValue must be array:
this.state = {
templateId:['some value']
}
<FormControl
multiple
type="select"
ref="templateSelect"
defaultValue={this.state.templateId}
onChange={this.handleTemplateChange}>
{options}
</FormControl>
With "react-bootstrap": "1.0.0-beta.14", the value prop is used:
<Form.Control as="select" value={user}>
{ users.map(opt => (<option>{ opt }</option>)) }
</Form.Control>
(Hi googlers!)
If you are attempting to load an array of options into the Form-Control (By a network-call, promise or other async function) make sure you dont render the Select-field until the options-array has been fully loaded. Or else the defaultValue wont work.
(True for react-bootstrap 1.0.0-beta.8. Your mileage may wary.)
This way you can set the default value.
<option >is any default</option>
{
dataoption.map(item => {
return <option key={item.Value} vlaue={item.Value} selected={defaultselect ? defaultselect == item.Value ? true : false : false} >{item.Text}</option>
})
}
</FormControl>
You may receive an error in the console:
Warning: Use the defaultValue or value props on instead of setting selected on .
But setting defaultValue or value does not solve your problem

Categories

Resources