I've been trying to use react-select component inside my react-json-schema-form and the select field does not update on the first time.
this is the behaviour I am getting.
Behaviour video
The code:
const CustomSelect = function (props) {
return (
<Select
id="input"
className="basic-single"
classNamePrefix="select"
options={props.options.enumOptions}
placeholder={props.label}
isSearchable={true}
onChange={(e) => {
props.onChange(e.value);
if(props.schema.title === "WiFi model"){
console.log(e.value)
}
}}
></Select>
);
};
The props are coming from RJSF, the enumOptions look like:
[{label: "3Com", value: "3Com"}
{label: "4G Systems", value: "4G Systems"}
{label: "7Links", value: "7Links"}
{label: "8devices", value: "8devices"}
{label: "ADB", value: "ADB"}
{label: "ADI Engineering", value: "ADI Engineering"}
{label: "ADSLR", value: "ADSLR"}
{label: "ALFA Network", value: "ALFA Network"}]
Can anyone tell me what am I doing wrong?
The Parent RJSF:
<Form
schema={schema}
widgets={widgets}
onSubmit={onSubmit}
formData={formData}
/>
That's the parent component, I am using react-select as a custom component!
const widgets = {
SelectWidget: CustomSelect,
};
For reference:
https://react-jsonschema-form.readthedocs.io/en/latest/advanced-customization/custom-widgets-fields/
Related
I have a set of select menus and I am trying to change a value when I select an option using onChange={updateValue} event. When I first select an option, the value is not being updated in the select menu.
It only changes the second time I try to choose an option. Not sure what I am doing wrong.
Edit: I did some more research (OnChange event using React JS for drop down) and I believe I need the value of the select to be updated as well, using setState. I cant figure out how to do it without having a variable for each value and set the state again.
let selectMenus = [
{
id: 'id1',
name: 'name1',
label: 'label1',
value: '0',
options: [
{
text: 'All ages',
value: '0',
},
{
text: '35 - 37 yrs',
value: '1',
},
],
buttonLabel: 'Refresh',
},
{
id: 'id2',
name: 'name2',
label: 'label2',
value: '1',
options: [
{
text: 'All ages',
value: '0',
},
{
text: '45 - 50 yrs',
value: '1',
},
],
buttonLabel: 'Refresh',
},
];
const [url, setUrl] = useState('http://localhost:5000/selectDropdowns1');
const updateValue = () => {
setUrl('http://localhost:5000/selectDropdowns2');
};
<form>
{selectMenus.map((select) => (
<div key={select.id} className='select-container'>
<label htmlFor={select.id}>{select.label}</label>
<select id={select.id} name={select.name} value={select.value} onChange={updateValue}>
{select.options.map((option) => (
<option value={option.value} key={uuid()}>
{option.text}
</option>
))}
</select>
<button>{select.buttonLabel}</button>
</div>
))}
</form>;
The problem is that when you provide onChange prop to select component it become a controlled component.
For more information: React Docs - Forms #controlled components
When you dealing with controlled components you must provide a value to it and when onChange triggerd it should update that value to work properly. Since you did not provide the full code, I imagine you have an array of select menus and options attached to it.
So in this case every select component should have own onChange method and own value to work properly. To achive this we should create another component for only Select Options. Like this;
function SelectComponent({ optionList, onSelected }) {
const [value, setValue] = useState();
const updateValue = ({ target }) => {
setValue(target.value);
if (onSelected) onSelected(target.value);
};
return (
<>
<label htmlFor={optionList.id}>{optionList.label}</label>
<select
id={optionList.id}
name={optionList.name}
value={value}
onChange={updateValue}
>
{optionList.options.map((option) => (
<option value={option.value} key={uuid()}>
{option.text}
</option>
))}
</select>
<button>{optionList.buttonLabel}</button>
</>
);
}
This component accepts to props; optionList and onSelected
optionList is the list of options to render
onSelected is a method that we call when user select and option
On main component, we should change the select section with our select component with props optionList and onSelected
return (
<div>
{selectMenus.map((select) => (
<div key={select.id} className="select-container">
<SelectComponent optionList={select} onSelected={updateValue} />
</div>
))}
</div>
);
So overall code is like this:
import { useState } from "react";
import { v4 as uuid } from "uuid";
export default function App() {
const [url, setUrl] = useState();
const updateValue = (value) => {
setUrl(value);
};
const selectMenus = [
{
id: 1,
label: "Menu 1",
name: "menu1",
buttonLabel: "Menu 1",
options: [
{
text: "option 1",
value: "option1"
},
{
text: "option 2",
value: "option2"
},
{
text: "option 3",
value: "option3"
}
]
},
{
id: 2,
label: "Menu 2",
name: "menu2",
buttonLabel: "Menu 2",
options: [
{
text: "option 1",
value: "option1"
},
{
text: "option 2",
value: "option2"
},
{
text: "option 3",
value: "option3"
}
]
},
{
id: 3,
label: "Menu 3",
name: "menu3",
buttonLabel: "Menu 3",
options: [
{
text: "option 1",
value: "option1"
},
{
text: "option 2",
value: "option2"
},
{
text: "option 3",
value: "option3"
}
]
}
];
return (
<div className="App">
<h1>URL Value: {url}</h1>
{selectMenus.map((select) => (
<div key={select.id} className="select-container">
<SelectComponent optionList={select} onSelected={updateValue} />
</div>
))}
</div>
);
}
function SelectComponent({ optionList, onSelected }) {
const [value, setValue] = useState();
const updateValue = ({ target }) => {
setValue(target.value);
if (onSelected) onSelected(target.value);
};
return (
<>
<label htmlFor={optionList.id}>{optionList.label}</label>
<select
id={optionList.id}
name={optionList.name}
value={value}
onChange={updateValue}
>
{optionList.options.map((option) => (
<option value={option.value} key={uuid()}>
{option.text}
</option>
))}
</select>
<button>{optionList.buttonLabel}</button>
</>
);
}
Working example is overhere codesandbox
Im trying to map a state property as value to a generated form input with react, but have been unsuccessful so far. The example below shows what im trying to do, but the wrong way as group.value is imported as a string, and wont be compiled into the value of the state variable. Does anyone know how to accomplish this? I need both the final object and property to be dynamically called (in this example frame and brand/year/model)
Map function:
return frameData.map((group) => {
return (
<div className="form-group">
<div className="col-sm-8">
<input
type="text"
className="form-control"
id={group.name}
name={group.name}
placeholder={group.placeHolder}
value={group.value}
onChange={(event) => this.updateValue(event)}
/>
</div>
</div>
);
});
dataset
const frameData = [
{
name: "frame-brand",
placeHolder: "Brand",
value: "{this.state.newSubmission.frame.brand}",
},
{
name: "frame-year",
placeHolder: "Year",
value: "{this.state.newSubmission.frame.year}",
},
{
name: "frame-model",
placeHolder: "Model",
value: "{this.state.newSubmission.frame.model}",
},
];
state
this.state = {
newSubmission:
frame: {
year: "",
model: "",
brand: "",
}
};
what you are trying to achieve is cool, but instead of passing the variable name i,e the entire state object, you can just pass the key and you can assign that to the <input /> component.
const frameData = [
{
name: "frame-brand",
placeHolder: "Brand",
value: "brand",
},
{
name: "frame-year",
placeHolder: "Year",
value: "year",
},
{
name: "frame-model",
placeHolder: "Model",
value: "model",
},
];
and in the map function, you can use
return frameData.map((group) => {
return (
<div className="form-group">
<div className="col-sm-8">
<input
type="text"
className="form-control"
id={group.name}
name={group.name}
placeholder={group.placeHolder}
value={this.state.newSubmission.frame[group.value]}
onChange={(event) => this.updateValue(event)}
/>
</div>
</div>
);
});
I made a component containing two dropdown lists. The options in the second dropdown menu is supposed to be filtered depending on the selected option from the first dropdown menu.
Now, I want to map a filtered array that is stored in a const similary to the way i map options1:
render() {
const options1 = [
{value: 'one', label: 'One'},
{value: 'two', label: 'Two'}
];
const options2 = [
{value: 'one-a', label: 'One A', link: 'one'},
{value: 'one-b', label: 'One B', link: 'one'},
{value: 'two-a', label: 'Two A', link: 'two'},
{value: 'two-b', label: 'Two B', link: 'two'}
];
const filteredOptions = options2.filter(o => o.link === this.state.selectedOption.value);
return (
<div style={style}>
<div>
<label>Select one</label>
<select
value={this.state.selectedOption.value}
onChange={this.handleChange1}
>
{options1.map(tag => <option>{tag.value}</option>)}
</select>
</div>
<div>
<label>Then the other</label>
<select
value={this.state.selectedOption2.value}
onChange={this.handleChange2}
>
{filteredOptions.map(tag => <option>{tag.value}</option>)}
</select>
</div>
</div>
)
}
The first mapping of options1 works just fine. However, my select tag gets rendered empty for the mapping of filteredOptions.
I have no idea why it won't work. Anyone happen to have an idea?
Full code: https://www.codepile.net/pile/evNqergA
Here is a working example for what you're trying to do.
import React, { Component } from "react";
const options1 = [
{ value: "one", label: "One" },
{ value: "two", label: "Two" }
];
const options2 = [
{ value: "one-a", label: "One A", link: "one" },
{ value: "one-b", label: "One B", link: "one" },
{ value: "two-a", label: "Two A", link: "two" },
{ value: "two-b", label: "Two B", link: "two" }
];
export default class SelectsComponent extends Component {
handleChange1 = e => {
console.log(e);
this.setState({
selectedOption: { value: e.target.value }
});
};
handleChange2 = e => {
this.setState({
selectedOption2: { value: e.target.value }
});
};
constructor(props) {
super(props);
this.state = {
selectedOption: { value: "one" },
selectedOption2: { value: "one-a" }
};
}
render() {
const filteredOptions = options2.filter(
o => o.link === this.state.selectedOption.value
);
return (
<div>
<div>
<label>Select one</label>
<select
value={this.state.selectedOption.value}
onChange={this.handleChange1}
>
{options1.map(tag => (
<option>{tag.value}</option>
))}
</select>
</div>
<div>
<label>Then the other</label>
<select
value={this.state.selectedOption2.value}
onChange={this.handleChange2}
>
{filteredOptions.map(tag => (
<option>{tag.value}</option>
))}
</select>
</div>
</div>
);
}
}
In your scenario filteredOptions would be an empty Array.
The check for o.link === this.state.selectedOption.value is doing something wrong.
Check the value of this.state.selectedOption.value, this is not set correctly.
The best way to do this wouldn't be inside of the render method.
1) move your arrays into state or other instance members
2) make sure to only trigger the sorting once
this.setState(lastState => ({
...lastState,
options2: lastState.options2.filter(yourFilterFn)
}))
3) map the filtered array into JSX inside of your render method
Side-note: this uses immutable setState (which I gather is important given you create a new filtered array from the options2 in your example). If you want to follow an even more functional pattern, you can do the filtering inside of your render method (although I don't recommend it). If you decided to filter inside of your render method, consider using a memoization technique from React 16.7 (which is currently in Alpha).
<el-tab-pane
v-for="(item, index) in systemOptionsHeaderList"
:key="index"
:name="item.value"
:lazy="true"
>
<span
slot="label"
>
{{item.label}}
<tips
v-if="formHasModify"
content="本页数据暂未保存"
/>
</span>
</el-tab-pane>
// The systemOptionsHeaderList:
systemOptionsHeaderList:
[{
label: '拨打策略',
value: '1'
}, {
label: '资源设置',
value: '2'
}, {
label: '打断设置',
value: '3'
}, {
label: '静默设置',
value: '4'
}, {
label: '分句设置',
value: '5'
}]
I use Element-ui "el-tab-pane" component
Now, I want use "formHasModify" (a Boolean Data) to control the "Tips"
component show or hidden
However, When I change the "formHasModify" the view can't update immediately
I solve this problem:
el-tab-pane
ref="elTabs" // lock the ref
// When The data change, force update the component
let childrenRefs = this.$refs.elTabs.$children
this.$nextTick(() => {
childrenRefs.forEach(child => child.$forceUpdate())
})
I try to implement example of material-ui selector and got code-fragment from github example
import React from "react";
import {withStyles} from "material-ui/styles";
import Select from "material-ui/Select";
import styles from "../../overtimesStyles";
class DivisionReport extends React.Component {
state = {
selectedOption: "",
}
handleChange = (selectedOption) => {
this.setState({selectedOption});
// selectedOption can be null when the `x` (close) button is clicked
if (selectedOption) {
console.log(`Selected: ${selectedOption.label}`);
}
}
render() {
const {selectedOption} = this.state;
return (
<Select
name="form-field-name"
value={selectedOption}
onChange={this.handleChange}
options={[
{value: "one", label: "One"},
{value: "two", label: "Two"},
{value: "333", label: "One"},
{value: "444", label: "444"},
{value: "555", label: "555"},
{value: "666", label: "666"},
]}
/>
);
}
}
export default withStyles(styles)(DivisionReport);
But there is no items for selection which I expected to see:
Can you suggest why I met with this issue?
p.s. top component, which use DivisionReport is:
const OvertimesReport = ({handleChangeSelection, selectedOption, classes}) => (
<div className={classes.workarea}>
<Card>
<CardContent className="overtimes">
<DivisionReport handleChangeSelection={handleChangeSelection} selectedOption={selectedOption}/>
</CardContent>
</Card>
</div>
);
style is:
workarea: {
margin: "0px 5%",
},
You are trying to import the Select component from material-ui/Select, but you should import if from the react-select package instead:
import Select from "react-select";
Also note that the value key of an option is named value and the label key is named label, not valueKey and labelKey.
<Select
name="form-field-name"
value={selectedOption}
onChange={this.handleChange}
options={[
{value: "one", label: "One"},
{value: "two", label: "Two"},
{value: "333", label: "One"},
{value: "444", label: "444"},
{value: "555", label: "555"},
{value: "666", label: "666"},
]
}
/>
import Select from '#material-ui/core/Select';
Then in the render:
<Select
native
name="form-field-name"
value={selectedOption}
onChange={this.handleChange}
>
<option key="1" value="1"> 1 </option>
<option key="2" value="2"> 2 </option>
<option key="3" value="3"> 3 </option>
</Select>
Try this way, and let me know how it works