I am using react-select for autocomplete and option related field. In single selection the below code works for only sending the value as string but in multiple it does not work though it works if i want the value as object.
Here is the code
const normalizedValue = (input, options, valueType, isMulti) => {
if (valueType === "string" && !isMulti) {
options.find(option => option.value === input.value);
}
if (valueType === "string" && isMulti) {
console.log("input value", input);
options.filter(option => option.value === input.value);
}
if (valueType === "object" && !isMulti) {
options.find(option => option === input.value);
}
if (valueType === "object" && isMulti) {
options.filter(option => option === input.value);
}
};
const SearchableTextField = ({
children,
input,
options,
isMulti,
valueType,
...props
}) => {
return (
<Select
{...props}
clearable={props.clearable}
searchable={props.searchable}
options={options}
{...input}
value={normalizedValue(input, options, valueType, isMulti)}
onChange={option =>
valueType === "string"
? input.onChange(option.value)
: input.onChange(option)
}
onBlur={() => input.onBlur(input.value)}
isMulti={isMulti}
/>
);
};
const MyForm = reduxForm({ form: "MyForm" })(
class extends React.PureComponent {
handleSubmit(values) {
console.log("values", values);
}
render() {
const { handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit(this.handleSubmit.bind(this))}>
<Field
name="myCoolSelect"
component={SearchableTextField}
options={[
{ value: "chocolate", label: "Chocolate" },
{ value: "strawberry", label: "Strawberry" },
{ value: "vanilla", label: "Vanilla" }
]}
valueType="string"
isMulti
/>
<button type="submit">SUbmit</button>
</form>
);
}
}
);
If i have the options as following
options = [
{value: 'abc', label: 'ABC'},
{value: 'abc2', label: 'ABC2'},
]
the expected output when i select suppose first option is selectedOption: "abc" in single selection which works and in multiple it should be selectedOption: ["abc", "abc2"] (this is not working)
I have a reproducible code as well in sandbox https://codesandbox.io/s/wnvpnlj245
The problem is here
onChange={option =>
valueType === "string"
? input.onChange(option.value)
: input.onChange(option)
}
You need also check here if its multi or not. Because when its multi it pass value as array.
Try like
onChange={option => {
valueType === "string"
? input.onChange(isMulti ? option.map(o => o.value) : option.value)
: input.onChange(option)
}
}
Related
I want the default value of my dropdown to be defaultValue={item.taste} (value from match.json) but it's not working... (go to /menu/Menu1 and Pea Soup)
import Select from "react-select";
export default function CustomDropdown({ style, options, defaultValue }) {
return (
<div style={style}>
<Select options={options} defaultValue={defaultValue} />
</div>
);
}
MenuItemDisplay:
export default function MenuItemDisplay() {
const { menuId, itemId } = useParams();
const { match } = JsonData;
const matchData = match.find((el) => el._id_menu === menuId)?._ids ?? [];
const item = matchData.find((el) => el._id === itemId);
const styles = {
select: {
width: "100%",
maxWidth: 150
}
};
const TASTE = [
{ label: "Good", value: "Good" },
{ label: "Medium", value: "Medium" },
{ label: "Bad", value: "Bad" }
];
...
return (
<>
<div className="TextStyle">
{"Taste "}
<CustomDropdown
style={styles.select}
options={TASTE}
defaultValue={item.taste}
//The default value is not working only if it's
//TASTE[0]
/>
</div>
...
</>
);
}
Here the link for the code
As defaultValue you need to pass one of the objects of the TASTE array. You can do this:
<CustomDropdown
style={styles.select}
options={TASTE}
defaultValue={TASTE.find(t => t.label === item.taste)}
/>
I have a nested array of objects, each object have a nested options array like this.
const [formFields, setFormFields ] = useState({
formTitle: '',
fields: [
{name: 'country', val: '', type: 'radio', options: ['Japan', 'Korea', 'usa'] },
{name: 'state', val: '', type: 'select', options: ['texas', 'florida']},
{name: 'location', val: '', type: 'text', options: []},
]})
Each of the items in the nested options array is supposed to be a value in a textInput which is editable.
I want to be able to add/remove/edit these values inside the textInput with a button click.
Please how will I be able to achieve this?
my code
<Containter>
{formFields.fields.map((field, index) => (
<View key={index}>
<View>
<TextInput
onChangeText={(value ) => {
onChange({name: field.name, value });
}}
value={field.name}
/>
</View>
{(field.type === 'select' || field.type === 'radio') && (
<>
{field.options.map((option) => (
<TextInput value={option}
onChangeText={(value ) => {
onChange({name: field.options, ...field.options, value });
}}
/>
<Text onPress={removeOption}>X</Text>
))}
<Button title="add option" />
</>
)
}
<IconButton
icon="delete"
onPress={handleRemoveField}
/>
</View>
))}
<Button
onPress={handleAddField}
title="Add"
/>
</Containter>
Add & remove implementation:
onAdd (index,value) {
const fields = formFields.fields.map((field,i) => {
if (i==index) {
const options = [...field.options,value]
return {...field, options}
}
return field
})
setFormFields(
{
...formFields,
fields
}
)
}
onRemove (index,value) {
const fields = formFields.fields.map((field,i) => {
if (i==index) {
const options = field.options.filter((item) => item != value)
return {...field, options}
}
return field
})
setFormFields(
{
...formFields,
fields
}
)
}
// in constructor
this.onChange = this.onChange.bind(this)
// in class
onChange (index,value) {
this.setState(state => {
const fields = state.fields.map((field,i) => {
if (i==index) field.val = value
return field
})
return {
...state,
fields
}
})
}
// in component
onChangeText( (e) => onChange(index, e.target.value) )
For value changing:
onChange (index,value) {
const fields = formFields.fields.map((field,i) => {
if (i==index) field.val = value
return field
})
setFormFields({
...formFields,
fields
})
}
...
// somewhere in input element
<TextInput ... onChangeText={(e) => onChange(index,e.target.value)} .. />
I am trying to create function that let user can select multiple options in react-select.
Here is my following code for onChange function:
const [searchUsers, setSearchUsers] = useState(false);
const [searchTeams, setSearchTeams] = useState(false);
const [searchOrganizations, setSearchOrganizations] = useState(false);
const find = (value) => {
value.forEach((option) => {
if (option.value === "Users" && value.length === 1) {
setSearchUsers(true);
setSearchTeams(false);
setSearchOrganizations(false);
} else if (option.value === "Teams" && value.length === 1) {
setSearchUsers(false);
setSearchTeams(true);
setSearchOrganizations(false);
} else if (option.value === "Organizations" && value.length === 1) {
setSearchUsers(false);
setSearchTeams(false);
setSearchOrganizations(true);
} else {
setSearchUsers(false);
setSearchTeams(false);
setSearchOrganizations(false);
}
});
};
const findList = [
{ value: "Users", label: "Users" },
{ value: "Teams", label: "Teams" },
{ value: "Organizations", label: "Organizations" },
];
const searchData = [];
findList.forEach((item) =>
searchData.push({ label: item.label, value: item.value })
);
and here is my Select tag:
<Select
className="search-select"
styles={customStyles}
instanceId="long-value-select"
closeMenuOnSelect={false}
components={animatedComponents}
defaultValue="Select"
isMulti
onChange={find}
options={searchData}
theme={(theme) => ({
...theme,
colors: {
...theme.colors,
neutral50: "#fff",
},
})}
/>
My onChange function is only getting one option value at a time right now. How can I make it take multiple option value?
If you want to update state from more than one selected option, you must rewrite you're find function:
const find = (value) => {
setSearchUsers(value.some((element) => element.value === "Users"));
setSearchTeams(value.some((element) => element.value === "Teams"));
setSearchOrganizations(value.some((element) => element.value === "Organizations")
);
};
FYI - Array.prototype.some()
The some() method tests whether at least one element in the array
passes the test implemented by the provided function
I am trying to watch a field which have watch:true field.In other words I want add useEffect dynamically.I have one json (which is coming from server).I want to watch field value (which have property watch:true).using it's value I want to update other field value .
here is my code
https://codesandbox.io/s/crimson-violet-uxbzd
see this object have watch: true, so I need to watch or check if it value is changed or not
{
label: "First",
name: "first",
type: "select",
remove: true,
watch: true,
options: ["", "one", "two"]
},
if it's value is changed then call this function
const getSecondDropdownValue = function(value) {
return new Promise((resolve, reject) => {
if (value === "one") {
setTimeout(function() {
resolve(["three", "four"]);
}, 1000);
}
if (value === "two") {
setTimeout(function() {
resolve(["five", "six"]);
}, 1000);
}
});
};
any update?.
Check if the item has watch property, if it does pass getSecondDropdownValue to onChange event of the select option. something like
<select onChange={hasWatch ? getSecondDropdownValue : () => {}}>...</select>
Create a component that will render select options.
// options = array of list options
// onChange = onchange event handler
// name = name of the select
const Option = ({ options, onChange, name }) =>
(options.length && (
<select onChange={(e) => onChange(e.target.value, name)}>
{Array.isArray(options) &&
options.map((option, index) => (
<option value={option} key={option + index}>{option}</option>
))}
</select>
)) ||
false;
Add useState for storing the data from the api.
// initial data from the api
const [data, updateData] = useState(apiData);
// update select options and the list
const updateSelectData = (list, updated) => {
const index = list.findIndex(item => item.name === updated.name);
return [
...list.slice(0, index),
Object.assign({}, list[index], updated),
...list.slice(index + 1)
];
};
getSecondDropdownValue function
const getSecondDropdownValue = function(value, name) {
const updated = data.find(
item => item.dependentField && item.dependentField[0] === name
);
// return new Promise((resolve, reject) => {
if (value === "one") {
// setTimeout(function() {
// resolve(["three", "four"]);
// }, 1000);
updated.options = ["three", "four"];
}
if (value === "two") {
// setTimeout(function() {
// resolve(["five", "six"]);
// }, 1000);
updated.options = ["five", "six"];
}
// });
updateData(updateSelectData(data, updated));
};
Example
// Get a hook function
const {useState} = React;
const apiData = [
{
label: "First",
name: "first",
type: "select",
watch: true,
options: ["", "one", "two"]
},
{
label: "Second",
name: "second",
options: [],
dependentField: ["first"],
type: "select"
}
];
// option component
const Option = ({ options, onChange, name }) =>
(options.length && (
<select onChange={(e) => onChange(e.target.value, name)}>
{Array.isArray(options) &&
options.map((option, index) => (
<option value={option} key={option + index}>{option}</option>
))}
</select>
)) ||
false;
function App() {
const [data, updateData] = useState(apiData);
const updateSelectData = (list, updated) => {
const index = list.findIndex(item => item.name === updated.name);
return [
...list.slice(0, index),
Object.assign({}, list[index], updated),
...list.slice(index + 1)
];
};
const getSecondDropdownValue = function(value, name) {
const updated = data.find(
item => item.dependentField && item.dependentField[0] === name
);
// return new Promise((resolve, reject) => {
if (value === "one") {
// setTimeout(function() {
// resolve(["three", "four"]);
// }, 1000);
updated.options = ["three", "four"];
}
if (value === "two") {
// setTimeout(function() {
// resolve(["five", "six"]);
// }, 1000);
updated.options = ["five", "six"];
}
// });
updateData(updateSelectData(data, updated));
};
return (
<div className="App">
{data.map((options, index) => (
<Option
name={options.name}
key={index}
onChange={options.watch ? getSecondDropdownValue : () => {}}
options={options.options}
/>
))}
</div>
);
}
// Render it
ReactDOM.render(
<App />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
i have implemented search filter to my react app, everything worked fine till i made select tag, to change search filter criteria.
class BookList extends Component {
state = {
search: '',
selectedValue: 'name',
options: [
{
name: 'Name',
value: 'name',
},
{
name: 'Author',
value: 'author',
},
{
name: 'ISBN',
value: 'isbn',
}
]
}
updateSearch (e) {
this.setState({search: e.target.value});
}
selectedValueHandler (e) {
this.setState({selectedValue: e.target.value});
}
render () {
if (this.state.selectedValue === 'name') {
let filteredBooks = this.props.books.filter(book => {
return book.name.toLowerCase().indexOf(this.state.search) !== -1;
})
} else if (this.state.selectedValue === 'author') {
let filteredBooks = this.props.books.filter(book => {
return book.author.toLowerCase().indexOf(this.state.search) !==
-1;
})
} else if (this.state.selectedValue === 'isbn') {
let filteredBooks = this.props.books.filter(book => {
return book.isbn.indexOf(this.state.search) !== -1;
})
}
return (
<div>
<div className='SearchInput'>
<input type='text'
value={this.state.search}
onChange={this.updateSearch.bind(this)} />
<select
id="searchSelect"
name="searchSelect"
onChange={this.selectedValueHandler.bind(this)} >
{this.state.options.map(item => (
<option key={item.value} value={item.value}>
{item.name}
</option>
))}
</select>
</div>
<div className='BookList'>
<ul>
{filteredBooks.map(book => {
return <Book key={book.book_id} name={book.name} author={book.author} isbn={book.isbn} />
})}
</ul>
</div>
</div>
)
}
};
export default BookList;
when i implement this code i am getting error: Line 69: 'filteredBooks' is not defined no-undef.
Tried to put this.state.selectedValue instead of name but it also doesn't work.
Any ideas how to fix issue?
let variables are locally scoped to the nearest wrapping curly braces. Define the variable above the if statements.
render () {
let filteredBooks;
if (this.state.selectedValue === 'name') {
filteredBooks = this.props.books.filter(book => {
return book.name.toLowerCase().indexOf(this.state.search) !== -1;
})
...
Unrelated, here's one way you could shorten your code:
const { books } = this.props;
const { search } = this.state;
const filteredBooks = books.filter(book =>
book[search].toLowerCase().includes(search)
)