Two React-Select dropdowns resetting each other - reactjs

I have two dropdown menus using React-Select:
<Select
className="type-select"
options={type_options}
onChange={filterType}
/>
<label id='filter-label'>Filter By Generation:</label>
<Select
className="gen-select"
options={generation_options}
onChange={filterGen}
/>
and the options for the two of these are as follows:
{ value: 'none', label: 'None'},
{ value: 'normal', label: 'Normal' },
{ value: 'fire', label: 'Fire' },
{ value: 'water', label: 'Water' },
{ value: 'grass', label: 'Grass' },
{ value: 'electric', label: 'Electric' },
{ value: 'ice', label: 'Ice' },
{ value: 'fighting', label: 'Fighting' },
{ value: 'poison', label: 'Poison' },
{ value: 'ground', label: 'Ground' },
{ value: 'flying', label: 'Flying' },
{ value: 'psychic', label: 'Psychic' },
{ value: 'bug', label: 'Bug' },
{ value: 'rock', label: 'Rock' },
{ value: 'ghost', label: 'Ghost' },
{ value: 'dark', label: 'Dark' },
{ value: 'dragon', label: 'Dragon' },
{ value: 'steel', label: 'Steel' },
{ value: 'fairy', label: 'Fairy' },
];
const generation_options = [
{ value: 'none', label: 'None'},
{ value: 'i', label: 'I'},
{ value: 'ii', label: 'II'},
{ value: 'iii', label: 'III'},
{ value: 'iv', label: 'IV'},
{ value: 'v', label: 'V'},
{ value: 'vi', label: 'VI'},
{ value: 'vii', label: 'VII'},
{ value: 'viii', label: 'VIII'},
]
Both of these dropdowns are stored in state using React Hooks, and in the onChange methods for both, the value for the given Select is updated and the other is reset to "none". Ex: If my first select is set to "Fire" then when I change my second select to Gen IV, the first select sets back to "none". Functionally, I already have this working and am able to use the React-Selects as I want, but the issue I'm having is that when updating one select, the other select won't change to show that it's been changed. I've tried adding value={type} where type is the state the first select corresponds to, but that just causes the React-Select component to never show the selected value. Does anyone know how to get these React-Selects to operate in the way I'm describing? I can elaborate on parts of this more if more explanation is needed.
Thanks!
Edit: Here are the filterType and filterGen methods:
function filterType(selectedOption) {
setType(selectedOption.value)
setGen("none")
if(selectedOption.value === "none") {
setPage(beginningUrl)
} else {
setPage(`https://pokeapi.co/api/v2/type/${selectedOption.value}`)
}
}
function filterGen(selectedOption) {
setGen(selectedOption.value)
setType("none")
if(selectedOption.value === "none") {
setPage(beginningUrl)
} else {
setPage(`generation-${selectedOption.value}`)
}
}

Figured it out. The issue was I was setting the state to just the value of the selected item, instead of the whole object (ie: {value: 'fire', label: 'Fire}. Fixed pieces of code below, with the changes in bold:
function filterType(selectedOption) {
**setType(selectedOption)**
**setGen({ value: 'none', label: 'None'})**
if(selectedOption.value === "none") {
setPage(beginningUrl)
} else {
setPage(`https://pokeapi.co/api/v2/type/${selectedOption.value}`)
}
}
function filterGen(selectedOption) {
**setGen(selectedOption)**
**setType({ value: 'none', label: 'None'})**
if(selectedOption.value === "none") {
setPage(beginningUrl)
} else {
setPage(`generation-${selectedOption.value}`)
}
}
<div className="select-container">
<label id='filter-label'>Filter By Type:</label>
<Select
className="type-select"
options={type_options}
onChange={filterType}
**value={type}**
/>
<label id='filter-label'>Filter By Generation:</label>
<Select
className="gen-select"
options={generation_options}
onChange={filterGen}
**value={gen}**
/>
</div>

Related

How can I return the corresponding clinic according to the selected province?

I need to select a province in the select to return the clinics that are located in that province, but when I select a province I get this error TypeError: termSearch.toLowerCase is not a function. I am a little lost in this part of the code, any help is appreciated.//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
import React, { useState, useEffect } from 'react'
import Select, { SingleValue } from 'react-select'
import { getClinic } from '../../api/drupalAPI'
import {Clinic} from '#icofcv/common';
export const SearchFilterClinics = () => {
////filter
type OptionType = {
value: string;
label: string;
};
const provincesList: OptionType[] = [
{ value: 'Todos', label: 'Todos' },
{ value: 'Alava/Araba', label: 'Alava/Araba' },
{ value: 'Albacete', label: 'Albacete' },
{ value: 'Alicante', label: 'Alicante' },
{ value: 'Almería', label: 'Almería' },
{ value: 'Avila', label: 'Avila' },
{ value: ' Badajoz', label: ' Badajoz' },
{ value: 'Islas Baleares', label: 'Islas Baleares' },
{ value: 'Barcelona', label: 'Barcelona' },
{ value: 'Burgos', label: 'Burgos' },
{ value: 'Cáceres', label: 'Cáceres' },
{ value: 'Cádiz', label: 'Cádiz' },
{ value: 'Castellón', label: 'Castellón' },
{ value: 'Ciudad Real', label: 'Ciudad Real' },
{ value: 'Córdoba', label: 'Córdoba' },
{ value: 'A Coruña/La Coruña', label: 'A Coruña/La Coruña' },
{ value: 'Cuenca', label: 'Cuenca' },
{ value: 'Gerona/Girona', label: 'Gerona/Girona' },
{ value: 'Granada', label: 'Granada' },
{ value: 'Guadalajara', label: 'Guadalajara' },
{ value: 'Gipuzkoa/Guipuzcoa', label: 'Gipuzkoa/Guipuzcoa' },
{ value: 'Huelva', label: 'Huelva' },
{ value: 'Huesca', label: 'Huesca' },
{ value: 'Jaen', label: 'Jaen' },
{ value: 'León', label: 'León' },
{ value: 'Lérida/Lleida', label: 'Lérida/Lleida' },
{ value: 'La Rioja', label: 'La Rioja' },
{ value: 'Lugo', label: 'Lugo' },
{ value: 'Madrid', label: 'Madrid' },
{ value: ' Málaga', label: ' Málaga' },
{ value: 'Murcia', label: 'Murcia' },
{ value: 'Navarra', label: 'Navarra' },
{ value: 'Orense/Ourense', label: 'Orense/Ourense' },
{ value: 'Asturias', label: 'Asturias' },
{ value: 'Palencia', label: 'Palencia' },
{ value: 'Las Palmas', label: 'Las Palmas' },
{ value: 'Pontevedra', label: 'Pontevedra' },
{ value: 'Salamanca', label: 'Salamanca' },
{ value: 'S.C.Tenerife', label: 'S.C.Tenerife' },
{ value: 'Cantabria', label: 'Cantabria' },
{ value: 'Segovia', label: 'Segovia' },
{ value: ' Sevilla', label: ' Sevilla' },
{ value: 'Soria', label: 'Soria' },
{ value: 'Tarragona', label: 'Tarragona' },
{ value: 'Teruel', label: 'Teruel' },
{ value: 'Toledo', label: 'Toledo' },
{ value: 'Valencia', label: 'Valencia' },
{ value: 'Valladolid', label: 'Valladolid' },
{ value: 'Bizkaia/Vizcaya', label: 'Bizkaia/Vizcaya' },
{ value: 'Zamora', label: 'Zamora' },
{ value: 'Zaragoza', label: 'Zaragoza' },
{ value: 'Ceuta', label: 'Ceuta' },
{ value: 'Melilla', label: 'Melilla' },
]
const [clinicList, setClinicList] = useState<Clinic[]>([]);
const [clinicListFilteredSelect, setClinicListFilteredSelect] = useState<Clinic[]>([]);
const [filterSelectClinic, setFilterSelectClinic] = useState<SingleValue<OptionType>>(provincesList[0]);
const fetchClinicList = async () => {
getClinic().then((response)=>{
console.log(response)
setClinicList(response);
setClinicListFilteredSelect(response)
}).catch ( (error) => {
console.error(error);
throw error;
});
}
const handleChangeSelect = (provinceList: SingleValue<OptionType>) =>{
console.log(provinceList)
setFilterSelectClinic(provinceList);
filterSelect(provinceList );
}
const filterSelect=(termSearch)=>{
const resultFilterSelect = clinicList.filter((element) => {
if(element.province?.toString().toLowerCase().includes(termSearch.toLowerCase() )
){
return element;
}
});
setClinicListFilteredSelect(resultFilterSelect);
}
useEffect (() => {
fetchClinicList();
}, []);
return (
<>
<div>
<h1>Encuentra tu clínica</h1>
</div>
<div>
<Select
defaultValue={filterSelectClinic}
options={provincesList}
onChange={handleChangeSelect}
/>
{
clinicListFilteredSelect.map((clinic) => (
<div>
<div>{clinic.title}</div>
<div>{clinic.propsPhone}</div>
<div>{clinic.mobile}</div>
<div>{clinic.email}</div>
<div>{clinic.province} </div>
<div>{clinic.registry}</div>
</div>
))
}
</div>
</>
)
}
In your filterSelect function, you are implicitly expecting termSearch to be a string termSearch: string. Looking at where filterSelect is used in handleChangeSelect it is passed this parameter provinceList: SingleValue<OptionType>.
Therefore the fix is:
termSearch.value.toLowerCase()
And putting it into context with your code, this line:
if(element.province?.toString().toLowerCase().includes(termSearch.value.toLowerCase() )
That's because SingleValue<OptionType> has the property getter .value

how to show only the result of the selection?

my current code shows a complete list of clinics, when in the selector I choose a province, it shows me the clinics located in that province, what I would like is not to show the complete list of clinics at the beginning but only show the result when filtering by province. That is to say, when starting the application that only the selection is seen and once the province is selected, the results will be shown.
import React, { useState, useEffect } from 'react'
import Select, { SingleValue } from 'react-select'
import { getClinic } from '../../api/drupalAPI'
import {Clinic} from '#icofcv/common';
export const SearchFilterClinics = () => {
////filter
type OptionType = {
value: string;
label: string;
};
const provincesList: OptionType[] = [
{ value: 'Todos', label: 'Todos' },
{ value: 'Alava/Araba', label: 'Alava/Araba' },
{ value: 'Albacete', label: 'Albacete' },
{ value: 'Alicante', label: 'Alicante' },
{ value: 'Almería', label: 'Almería' },
{ value: 'Avila', label: 'Avila' },
{ value: ' Badajoz', label: ' Badajoz' },
{ value: 'Islas Baleares', label: 'Islas Baleares' },
{ value: 'Barcelona', label: 'Barcelona' },
{ value: 'Burgos', label: 'Burgos' },
{ value: 'Cáceres', label: 'Cáceres' },
{ value: 'Cádiz', label: 'Cádiz' },
{ value: 'Castellón', label: 'Castellón' },
{ value: 'Ciudad Real', label: 'Ciudad Real' },
{ value: 'Córdoba', label: 'Córdoba' },
{ value: 'A Coruña/La Coruña', label: 'A Coruña/La Coruña' },
{ value: 'Cuenca', label: 'Cuenca' },
{ value: 'Gerona/Girona', label: 'Gerona/Girona' },
{ value: 'Granada', label: 'Granada' },
{ value: 'Guadalajara', label: 'Guadalajara' },
{ value: 'Gipuzkoa/Guipuzcoa', label: 'Gipuzkoa/Guipuzcoa' },
{ value: 'Huelva', label: 'Huelva' },
{ value: 'Huesca', label: 'Huesca' },
{ value: 'Jaen', label: 'Jaen' },
{ value: 'León', label: 'León' },
{ value: 'Lérida/Lleida', label: 'Lérida/Lleida' },
{ value: 'La Rioja', label: 'La Rioja' },
{ value: 'Lugo', label: 'Lugo' },
{ value: 'Madrid', label: 'Madrid' },
{ value: ' Málaga', label: ' Málaga' },
{ value: 'Murcia', label: 'Murcia' },
{ value: 'Navarra', label: 'Navarra' },
{ value: 'Orense/Ourense', label: 'Orense/Ourense' },
{ value: 'Asturias', label: 'Asturias' },
{ value: 'Palencia', label: 'Palencia' },
{ value: 'Las Palmas', label: 'Las Palmas' },
{ value: 'Pontevedra', label: 'Pontevedra' },
{ value: 'Salamanca', label: 'Salamanca' },
{ value: 'S.C.Tenerife', label: 'S.C.Tenerife' },
{ value: 'Cantabria', label: 'Cantabria' },
{ value: 'Segovia', label: 'Segovia' },
{ value: ' Sevilla', label: ' Sevilla' },
{ value: 'Soria', label: 'Soria' },
{ value: 'Tarragona', label: 'Tarragona' },
{ value: 'Teruel', label: 'Teruel' },
{ value: 'Toledo', label: 'Toledo' },
{ value: 'Valencia', label: 'Valencia' },
{ value: 'Valladolid', label: 'Valladolid' },
{ value: 'Bizkaia/Vizcaya', label: 'Bizkaia/Vizcaya' },
{ value: 'Zamora', label: 'Zamora' },
{ value: 'Zaragoza', label: 'Zaragoza' },
{ value: 'Ceuta', label: 'Ceuta' },
{ value: 'Melilla', label: 'Melilla' },
]
const [clinicList, setClinicList] = useState<Clinic[]>([]);
const [clinicListFilteredSelect, setClinicListFilteredSelect] = useState<Clinic[]>([]);
const [filterSelectClinic, setFilterSelectClinic] = useState<SingleValue<OptionType>>(provincesList[0]);
const fetchClinicList = async () => {
getClinic().then((response)=>{
console.log(response)
setClinicList(response);
setClinicListFilteredSelect(response)
}).catch ( (error) => {
console.error(error);
throw error;
});
}
const handleChangeSelect = (provinceList: SingleValue<OptionType>) =>{
console.log(provinceList)
setFilterSelectClinic(provinceList);
filterSelect(provinceList );
}
const filterSelect=(termSearch)=>{
const resultFilterSelect = clinicList.filter((element) => {
if(element.province?.toString().toLowerCase().includes(termSearch.value.toLowerCase() )
){
return element;
}
});
setClinicListFilteredSelect(resultFilterSelect);
}
useEffect (() => {
fetchClinicList();
}, []);
return (
<>
<div>
<h1>Encuentra tu clínica</h1>
</div>
<div>
<Select
defaultValue={filterSelectClinic}
options={provincesList}
onChange={handleChangeSelect}
/>
{
clinicListFilteredSelect.map((clinic) => (
<div>
<div>{clinic.title}</div>
<div>{clinic.propsPhone}</div>
<div>{clinic.mobile}</div>
<div>{clinic.email}</div>
<div>{clinic.province} </div>
<div>{clinic.registry}</div>
</div>
))
}
</div>
</>
)
}
You're setting clinicListFilteredSelect in your useEffect with an empty dependancy array. This means when your component mounts it will set clinicListFilteredSelect to the response of your API call.
If you instead want to only show the response when filterting by clinics, you need to move this call to the event handler and remove the useEffect.
Something like:
const handleChangeSelect = async (provinceList: SingleValue<OptionType>) => {
getClinic().then((response) => {
setClinicList(response);
setClinicListFilteredSelect(response)
setFilterSelectClinic(provinceList);
filterSelect(provinceList );
}).catch ((error) => {
console.error(error);
throw error;
});
}

How can I add the first to the array in useState in React?

I have an array object called tempArr.
When I run a function called fetchLifeCycle, i want to act setPickerItems(tempArr); is mandatory first, and then setPickerItems([...tempArr, { label: 'Not selected', value: "NONE" }]) .
As a result, { label: 'not selected', value: "NONE" } was added at the end.
But I want to add that object to the first object of pickerItems. How do I do that?
this is my code
tempArr = [
{ label: "hi", value: 'egg' },
{ label: "bye", value: 'insect' },
{ label: "woo", value: 'pest' },
{ label: "pick", value: 'hambuger' },
]
const [pickerItems, setPickerItems] = useState([])
const fetchLifeCycle = () => {
setPickerItems(tempArr);
setPickerItems([...tempArr, { label: '선택안함', value: "NONE" }])
}
expected answer
pickerItems = [
{ label: '선택안함', value: "NONE" }
{ label: "hi", value: 'egg' },
{ label: "bye", value: 'insect' },
{ label: "woo", value: 'pest' },
{ label: "pick", value: 'hambuger' },
]
tempArr = [
{ label: "hi", value: 'egg' },
{ label: "bye", value: 'insect' },
{ label: "woo", value: 'pest' },
{ label: "pick", value: 'hambuger' },
]
const updated = [{ label: '선택안함', value: "NONE" }, ...tempArr]
console.log('updated', updated)
You just have to reverse the order:
setPickerItems([{ label: '선택안함', value: "NONE" }, ...tempArr])
You can put the new item first, then spread the existing array:
setPickerItems([{ label: '선택안함', value: "NONE" }, ...tempArr])
Just simple:
setPickerItems([{ label: '선택안함', value: "NONE" }, ...tempArr])

TypeScript Error: Adding type to .map Array of Objects

I'm trying to add types to the Item object that is returned from unitedStates.map((item: Item) => {}, but I can't seem to correctly add the types.
I've defined the unitedStates array of objects as unitedStates: Array<UnitedStates> and that seems to be correct, but I'm not sure why Item is not correctly typed.
Error:
src/StatePicker.tsx:198:35 - error TS2345: Argument of type '(item: Item) => JSX.Element' is not assignable to parameter of type '(value: UnitedStates, index: number, array: UnitedStates[]) => Element'.
Types of parameters 'item' and 'value' are incompatible.
Type 'UnitedStates' is not assignable to type 'Item'.
{unitedStates.map((item: Item) => {
Types.tsx:
// TypeScript: Types
interface Props {
title: string,
onPress: Function,
onValueChange: Function,
}
interface Item {
label: string,
value: string,
key: number | string,
color: string,
};
interface UnitedStates {
label: string,
value: string,
};
United States (Array of Objects):
// United States
const unitedStates: Array<UnitedStates> = [
{ label: 'AL', value: 'AL' },
{ label: 'AK', value: 'AK' },
{ label: 'AZ', value: 'AZ' },
{ label: 'AR', value: 'AR' },
{ label: 'CA', value: 'CA' },
{ label: 'CO', value: 'CO' },
{ label: 'CT', value: 'CT' },
{ label: 'DE', value: 'DE' },
{ label: 'FL', value: 'FL' },
{ label: 'GA', value: 'GA' },
{ label: 'HI', value: 'HI' },
{ label: 'ID', value: 'ID' },
{ label: 'IL', value: 'IL' },
{ label: 'IN', value: 'IN' },
{ label: 'IN', value: 'IN' },
{ label: 'KS', value: 'KS' },
{ label: 'KY', value: 'KY' },
{ label: 'LA', value: 'LA' },
{ label: 'ME', value: 'ME' },
{ label: 'MD', value: 'MD' },
{ label: 'MA', value: 'MA' },
{ label: 'MI', value: 'MI' },
{ label: 'MN', value: 'MN' },
{ label: 'MS', value: 'MS' },
{ label: 'MO', value: 'MO' },
{ label: 'MT', value: 'MT' },
{ label: 'NE', value: 'NE' },
{ label: 'NV', value: 'NV' },
{ label: 'NH', value: 'NH' },
{ label: 'NJ', value: 'NJ' },
{ label: 'NM', value: 'NM' },
{ label: 'NY', value: 'NY' },
{ label: 'NC', value: 'NC' },
{ label: 'ND', value: 'ND' },
{ label: 'OH', value: 'OH' },
{ label: 'OK', value: 'OK' },
{ label: 'OR', value: 'OR' },
{ label: 'PA', value: 'PA' },
{ label: 'RI', value: 'RI' },
{ label: 'SC', value: 'SC' },
{ label: 'SD', value: 'SD' },
{ label: 'TN', value: 'TN' },
{ label: 'TX', value: 'TX' },
{ label: 'UT', value: 'UT' },
{ label: 'VT', value: 'VT' },
{ label: 'VA', value: 'VA' },
{ label: 'WA', value: 'WA' },
{ label: 'WV', value: 'WV' },
{ label: 'WI', value: 'WI' },
{ label: 'WY', value: 'WY' },
];
Render Method:
// Render iOS Picker
const renderIOSPicker = () => {
try {
return (
<Picker
selectedValue={state}
onValueChange={selectState}>
{unitedStates.map((item: Item) => {
return (
<Picker.Item
label={item.label}
value={item.value}
key={item.key || item.label}
color={item.color}
/>
);
})}
</Picker>
)
}
catch (error) {
console.log(error);
}
};
You're mapping UnitedStates[] but you are type-hinting the item(a UnitedStates object) in the callback as Item.
Maybe you want this
{unitedStates.map((item: UnitedStates) => {
return (
<Picker.Item
label={item.label}
value={item.value}
key={item.key || item.label}
color={item.color}
/>
);
})}
Or this
{myItems.map((item: Item) => {
return (
<Picker.Item
label={item.label}
value={item.value}
key={item.key || item.label}
color={item.color}
/>
);
})}
The issue was that I was incorrectly typing the unitedStates object with Array<UnitedStates>, so the code should look like the following:
Types:
interface UnitedStates {
[index: number]: { label: string; value: string };
map: Function,
}
unitedStates Typing:
const unitedStates: UnitedStates = [];

Cannot use multiple radiogroups in react-materialize

So, I am building an avatar creator for my application, and I have a Carousel that holds multiple RadioGroups, I can slide to the new radiogroup, but when I click on any of the other radio groups (except for the first one) and it will only target the value from the first one.
<Carousel options={{ fullWidth: true, indicators: true }} className="black-text center">
<div>
<RadioGroup
name="hair"
label="Hair Selection"
value={this.state.avatar.topType}
onChange={this.handleAvatarChange}
options={[{ label: 'Eyepatch', value: 'Eyepatch' }, { label: 'Long
Hair', value: 'LongHairStraight2' }, { label: 'Medium Hair', value:
'LongHairNotTooLong' }, { label: 'Short Hair', value:
'ShortHairShortFlat' }, { label: 'Short Dreads', value:
'ShortHairDreads01' }, { label: 'Balding', value: 'ShortHairSides'
}]}
/>
</div>
<div>
<RadioGroup
name="hairColor"
label="Hair Selection"
value={this.state.avatar.hairColor}
onChange={this.handleAvatarChange}
options={[{ label: 'Brown', value: 'Brown' }, { label: 'Blonde',
value: 'Blonde' }, { label: 'Red', value:
'Red' }, { label: 'Gray', value:
'Gray' }, { label: 'Black', value:
'Black' }, { label: 'Auburn', value: 'Auburn'
}]}
/>
</div>
</Carousel>
handleAvatarChange = (event) => {
let selection = event.target.value;
let type = event.target.name;
console.log(selection, 'event')
console.log(type, 'type');
let avatarCopy = { ...this.state.avatar };
if (type === 'hair') {
avatarCopy.topType = selection
this.setState({ avatar: avatarCopy });
} else if (type === 'hairColor') {
avatarCopy.hairColor = selection;
this.setState({ avatar: avatarCopy });
}
}
I expect that when I slide to the next slide on the carousel, that I can change the state based on the type that was passed back to my handleAvatarChange function. Instead, all of the slides are changing the values based on the first slide.
It seems like the autogenerated id numbering for the options resets to 0 for each radiogroup, even if they are in the same DOM.
Therefore, each n input of the radio groups has the same id (<input id="radio0" type="radio" value="1" checked="">).
I have reported the bug here.
As a workaround, I'm requesting an "id" prop, and if not found, concatenating the component's name to the index to build the id's.
var RadioGroup = function RadioGroup(_ref) {
var onChange = _ref.onChange,
withGap = _ref.withGap,
disabled = _ref.disabled,
name = _ref.name,
radioClassNames = _ref.radioClassNames,
value = _ref.value,
options = _ref.options;
return _react.default.createElement(_react.default.Fragment, null, options.map(function (radioItem, idx) {
return _react.default.createElement("label", {
className: radioClassNames,
htmlFor: radioItem.id ? radioItem.id : "radio".concat(radioItem.name).concat(idx),
key: radioItem.id ? radioItem.id : "radio".concat(radioItem.name).concat(idx)
}, _react.default.createElement("input", {
id: radioItem.id ? radioItem.id : "radio".concat(radioItem.name).concat(idx),
value: radioItem.value,
type: "radio",
checked: radioItem.value === value,
name: name,
onChange: onChange,
disabled: disabled,
className: (0, _classnames.default)({
'with-gap': withGap
})
}), _react.default.createElement("span", null, radioItem.label));
}));
};

Resources