I had a react-select rendering a list of emails, and i need to keep the selected emails as a default option when the email is selected and saved, but the defaultValues are not working. How can i do that?
Here is my select component:
const [selectedOption, setSelectedOption] = useState("")
const makeEmailOption = item => ({
value: item.id,
label: item.ccEmail,
id: item.id,
chipLabel: item.ccEmail,
rest: item,
selected: item.selected
})
const makeEmailOptions = items => items.map(makeEmailOption)
const handleChange = (value) => {
setSelectedOption(value)
props.emails(value)
}
return (
<div>
<Select
multi={true}
name={props.name}
options={makeEmailOptions(props.ccemailfilter)}
onChange={handleChange}
value={selectedOption}
/>
</div>
)
I receive everything as props and work with that to make the options. How can i do that to make the default value if a field selected is true?
You almost have it, but in this case, you are setting the value to the selectedOption instead of setting the defaultValue. Also, you are changing the default value each time there is a change, which shouldn't be needed.
const defaultVal = {value: selectedOption, label: selectedOption};
return (
<div>
<Select
multi={true}
name={props.name}
options={makeEmailOptions(props.ccemailfilter)}
defaultValue={defaultVal}
/>
</div>
)
I came with the following solution, since my component use a function to set some variables to the select, i use a useEffect to call that with a filter right after the page render.
useEffect(() => {
handleChange(makeEmailOption(props.ccemailfilter.filter(x => x.selected)))
}, [])
const handleChange = (value) => {
setSelectedOption(value)
props.emails(value)
}
So, the handleChange are called on the onChange of the select and once after the page loads, to create a value to the select to use.
Related
I'm trying out React and trying to make a simple component. Input and "Add" button.
I want get a list of values after filling in the input and clicking on the button. I can see that the state is getting filled, but I don't understand why the list is not being rerender.
Here is my code https://jsfiddle.net/3hkm2qnL/14/
`
const InputWithAddBtn = props => {
const [ value, setValue ] = React.useState('');
return (
<div>
<input type="text" value={value} onChange={e => setValue(e.target.value)} />
<button onClick={() => props.add(value)}>+</button>
</div>
);
};
`
The problem is in the add() function, which by pushing onto the original array does not signal to the component to rerender.
const add = (value) => {
initValue.push(value)
console.log(initValue)
setValue(initValue)
}
One possible solution:
const add = (value) => {
const newValues = [...initValue, value]
console.log(newValues)
setValue(newValues)
}
That will trigger correctly the component to rerender.
For more info see https://stackoverflow.com/a/67354136/21048989
Cheers
I am trying to follow this sample:
https://react-select.com/home
I have defined two following arrays:
const [selectedAccountTypes, setSelectedAccountTypes] = useState([]);
const [allAccountTypes, setAllAccountTypes] = useState([]);
I set the values in following function when a user click a button:
const onAccountChanged = async (val) => {
console.log(val);
setSearchedAccount(val);
console.log('onAccountChanged');
console.log(searchedAccountValue);
const accounts = await getAccountTypeLookupList();
var options = accounts.data.map((o) => ({
label: o.name,
value: o.id,
number: o.accountNumber
}))
console.log(options);
setAllAccountTypes(options);
setSelectedAccountTypes(options);
console.log(options);
console.log(setSelectedAccountTypes);
return options;
};
And my component:
<Select
placeholder='First select account above'
isMulti
cacheOptions
defaultValue ={selectedAccountTypes}
options={allAccountTypes}/>
I would expect all to be selected in this scenario nut nothing is default selected. Am i missing something?
Set the value to selectedAccountTypes, then onChange call the setSelectedAccountTypes to update it.
The issue was the timing. At time I set the selected values the control has not finished rendering the control. I ended up making a useEffect on the array, and in that I set default selected.
I am having a simple form in React, which looks like:
const [placeOptions] = useState([
{ value: 'USA', label: 'USA' },
{ value: 'MEX', label: 'Mexico' },
]);
const [name, setName] = useState('');
const [place, setPlace] = useState('USA');
....
<input onChange={event => setName(event.target.value)} type="text"/>
<select onChange={event => setPlace(event.target.value)}>
{placeOptions.map(item => (
<option key={item.value} value={item.value}>
{item.label}
</option>
))}
</select>
<CustomButton id="custom-btn" props={[name, place]} />
The above Custom button is just rendering once and is taking the default null and 'USA' value. It should Ideally send props to every event change, possibly refreshing the component once event is triggered. I am unable to determine how do I refresh a component on event change and pass the correct state to the props.
Edit: The below is the CustomButton.tsx file:
export function CustomButton({ props, id }: { props?:any, id?:string}) {
var name = props ? props[0] : '';
var place = props ? props[1] : '';
useEffect(() => {
renderButton(id);
}
return(
<React.Fragment>
<div id={id}></div>
</React.Fragment>
);
async function renderButton(id: string) {
... // Some logic involving the props passed
}
}
Edit 2:
This the code sandbox: https://codesandbox.io/s/amazing-dust-315dk?file=/src/App.js
All I want is to change the props too and dynamically render the custom button.
The problem is how you define the name and the place variable in CustomButton Component.
Variables of javascript defined like var let, and const will not trigger re-renders in React Button. States and Props only can trigger re-renders in React Components.
So if you do something like this in the parent file:
// All same code excepet
<CutomButtom id="cutom-btn" name={name} place={place} />
You can get name and place directly from props and use them as it is like:
export function CustomButton({ name, place, id }: { name: string, place: string, id:string}){
// NO need for defining name and place now, just use them directly...
}
Another improvement you can make is to define PropsType separately:
export interface CustomButtonProps {
id: string;
name:string;
place:string;
}
export function CustomButton({name, place, id}:CustomButtonProps){
}
I am building a rest countries app in react and I'm trying to filter by region through a list. I would like to use a dropdown menu. I have tried using react select, creating a new component, this is my code
const DropdownMenu = (props) => {
const options = [
'', 'Africa', 'America', 'Asia', 'Europe', 'Oceania'
];
const defaultOption = options[0];
const handleFilterInput = (event) => {
let value = event.target.options;
props.handleRegionSearch(value);
};
return(
<div>
<Select options={options} onChange={handleFilterInput} value={defaultOption} placeholder="Select a region"/>
</div>
)
}
export default DropdownMenu;
My problem is, I can not get the value to change to the option selected, therefore it's impossible for me to filter the list I took from the api.
Does anyone have a possible solution for this?
Thanks
You are setting the value wrong
value={defaultOption}
This default option is never changing.
What you can do is catch the current value from onChange() and set the value somewhere (preferably in state) and then use that stored value as the value of dropdown component
Something like this.
const options = [
'', 'Africa', 'America', 'Asia', 'Europe', 'Oceania'
];
const [selectedValue , setSelectedValue ] = useState(options[0]);
const handleFilterInput = (event) => {
let value = event.target.options;
setSelectedValue(value);
props.handleRegionSearch(value);
};
return(
<div>
<Select options={options} onChange={handleFilterInput} value={selectedValue} placeholder="Select a region"/>
</div>
)
Thanks! In the end, I used a react-select component which did solve a this issue on it's own.
I have this React Component like below:
const ProductCell = (props) => {
const [option, setOption] = useState();
return(
<div>
<NativeSelect
value={option}
onChange={e => setOption(e.target.value)}
>
{props.product.variations.nodes.map( // here I extracted all the item
(item, i) => (
<option value={item} key={i}>{item.name}</option> // set option value to item object
))
}
</NativeSelect>
<Typography variant="h5" className={classes.title}>
{option.price} // I want to update the value of price according to the selected option.
</Typography> // according to the selected option above
</div>
)
}
I have a NativeSelect component which is from React Material-Ui, so basically it is a Select html tag. In the code above, what I do is, extract all the element inside props.product.variations.nodes and put all the extracted item and put each of the element into a <options/> tag.
The Json object for item will look like this:
"variations": {
"nodes": [
{
"id": "someId",
"name": "abc1234",
"variationId": 24,
"price": "$100.00"
},
{
.. another ID, name,variation and price
}
]
}
As you can see, I targeting the part of id, name , variationId and price as an object. Therefore each <option/> tag will present with item.name as the presentation to user. So far in this part having no problem, let say having 5 variations, and can present all of them.
What I want to do is:
I want to update the value of price under the <Typography /> component. Example, user selected 3rd options in the Select, I want to update the price value of the 3rd item in <Typography /> .
What I tried:
I create a react hooks const [option, setOption] = useState(); , then when handleChange, I setOption() with event.target.value in NativeSelect component . Therefore the value of <option /> tag is set as item object.
Lastly, I get the price value from the hooks in the Typography section.
But what I get is:
The price value is undefined in console log. So I can't get the value of option.price.
and this error:
TypeError: Cannot read property 'price' of undefined
Question:
How can I get the option.price value(which I expect it is same with item.price) outside the NativeSelect component in my above example?
I tried my best to explain based on what I understand by this time being. So any help will be well appreciated.
Update:
Here is what I got when console log the item object in variation.node.map() section and data object inside onHandleChanged section, but also produce the same result:
You have to set a default selected option on your ProductCell component. Also your onChange handler will receive a string instead of an object when you access the value on event.target.value.
From the docs
function(event: object) => void event: The event source of the callback. You can pull out the new value by accessing event.target.value (string).
event.target.value will be a string even though you pass the value as object on NativeSelect component.
What you might want to do? Don't set the current selected item as an object, instead use the id and have a function that look-ups the item using the current selected id.
Check the code below.
const ProductCell = (props) => {
const { variations } = props.product;
const { nodes } = variations;
// we're setting the first node's id as selected value
const [selectedId, setSelectedId] = useState(nodes[0].id);
const getSelectedPrice = () => {
// finds the node from the current `selectedId`
// and returns `price`
const obj = nodes.find((node) => node.id === selectedId);
return obj.price;
};
function onChange(event) {
// event.target.value will be the id of the current
// selected node
setSelectedId(parseInt(event.target.value));
}
return (
<div>
<NativeSelect value={selectedId} onChange={onChange}>
{nodes.map((item, i) => (
<option value={item.id} key={i}>
{item.name}
</option>
))}
</NativeSelect>
<Typography variant="h5">{getSelectedPrice()}</Typography>
</div>
);
};
Also notice that were passing the id as a value prop on each of our options.
<option value={item.id} key={i}>
And how we now display the price, we're calling our getSelectedPrice().
Update
I thought a better solution. I realized that you can set your selected state as an object and on your onChange handler given the id from event.target.value find the item on nodes and set that as your new selected state.
const ProductCell = (props) => {
const { variations } = props.product;
const { nodes } = variations;
const [selected, setSelected] = useState(nodes[0]);
function onChange(event) {
const value = parseInt(event.target.value);
setSelected(nodes.find((node) => node.id === value));
}
return (
<div>
<NativeSelect value={selected.id} onChange={onChange}>
{nodes.map((item, i) => (
<option value={item.id} key={i}>
{item.name}
</option>
))}
</NativeSelect>
<Typography variant="h5">{selected.price}</Typography>
</div>
);
};