How to programmatically clear/reset React-Select? - javascript

ReactSelect V2 and V3 seems to have several props like clearValue, resetValue and setValue. Whatever I'm trying, I'm not able to clear the selections programmatically. resetValue seems not to be accessible from the outside.
selectRef.setValue([], 'clear')
// or
selectRef.clearValue()
This does not clear the current selection.
Do I miss something here or is it not fully implemented yet?

I came across this problem myself and managed to fix it by passing a key to the React-Select component, with the selected value appended to it. This will then force the ReactSelect to re-render itself when the selection is updated.
I hope this helps someone.
import ReactSelect from 'react-select';
...
<ReactSelect
key={`my_unique_select_key__${selected}`}
value={selected || ''}
...
/>

If you're using react-select you can try to pass null to value prop.
For example:
import React from "react";
import { render } from "react-dom";
import Select from "react-select";
class App extends React.Component {
constructor(props) {
super(props);
const options = [
{ value: "one", label: "One" },
{ value: "two", label: "Two" }
];
this.state = {
select: {
value: options[0], // "One" as initial value for react-select
options // all available options
}
};
}
setValue = value => {
this.setState(prevState => ({
select: {
...prevState.select,
value
}
}));
};
handleChange = value => {
this.setValue(value);
};
handleClick = () => {
this.setValue(null); // here we reset value
};
render() {
const { select } = this.state;
return (
<div>
<p>
<button type="button" onClick={this.handleClick}>
Reset value
</button>
</p>
<Select
name="form-field-name"
value={select.value}
onChange={this.handleChange}
options={select.options}
/>
</div>
);
}
}
render(<App />, document.getElementById("root"));
Here's a working example of this.

You can clear the value of react select using the ref.
import React, { useRef } from "react";
import Select from "react-select";
export default function App() {
const selectInputRef = useRef();
const onClear = () => {
selectInputRef.current.select.clearValue();
};
return (
<div className="App">
<h1>Select Gender</h1>
<Select
ref={selectInputRef}
options={[
{ value: "male", label: "Male" },
{ value: "female", label: "Female" }
]}
/>
<button onClick={onClear}>Clear Value</button>
</div>
);
}
Here is the CodeSandbox link

Just store the value in the state, and change the state programmatically using componentDidUpdate etc...
class Example extends Component {
constructor() {
super()
}
state = {
value: {label: 'Default value', key : '001'}
}
render() {
return(
<Select
...
value={this.state.value}
...
/>
)
)}
Note: 'value' should be an object.

A simple option would be to pass null to the value prop.
<Select value={null} />

This is my working implementation of a React-Select V3 cleared programmatically with Hooks.
You can play with it in the CodeSandbox DEMO. Any feedback is welcome.
const initialFormState = { mySelectKey: null };
const [myForm, setMyForm] = useState(initialFormState);
const updateForm = value => {
setMyForm({ ...myForm, mySelectKey: value });
};
const resetForm = () => {
setMyForm(initialFormState);
};
return (
<div className="App">
<form>
<Select name = "mySelect"
options = {options}
value = {options.filter(({ value }) => value === myForm.mySelectKey)}
getOptionLabel = {({ label }) => label}
getOptionValue = {({ value }) => value}
onChange = {({ value }) => updateForm(value)} />
<p>MyForm: {JSON.stringify(myForm)}</p>
<input type="button" value="Reset fields" onClick={resetForm} />
</form>
</div>
);

If someone looking for solution using Hooks. React-Select V3.05:
const initial_state = { my_field: "" }
const my_field_options = [
{ value: 1, label: "Daily" },
{ value: 2, label: "Weekly" },
{ value: 3, label: "Monthly" },
]
export default function Example(){
const [values, setValues] = useState(initial_state);
function handleSelectChange(newValue, actionMeta){
setValues({
...values,
[actionMeta.name]: newValue ? newValue.value : ""
})
}
return <Select
name={"my_field"}
inputId={"my_field"}
onChange={handleSelectChange}
options={my_field_options}
placeholder={values.my_field}
isClearable={true}
/>
}

Along the top answer, please note that the value needs to be "null" and not "undefined" to clear properly.

If you check Select component in React Developers panel you will see that it is wrapped by another – State Manager. So you ref is basically ref to State manager, but not to Select itself.
Luckily, StateManager has state) and a value object which you may set to whatever you want.
For example (this is from my project, resetGroup is onClick handler that I attach to some button in DOM):
<Select onChange={this.handleGroupSelect}
options={this.state.groupsName.map(group =>
({ label: group, value: group }) )}
instanceId="groupselect"
className='group-select-container'
classNamePrefix="select"
placeholder={this.context.t("Enter name")}
ref={c => (this.groupSelect = c)}
/>
resetGroup = (e) => {
e.preventDefault()
this.setState({
selectedGroupName: ""
})
this.groupSelect.state.value.value = ""
this.groupSelect.state.value.label = this.context.t("Enter name")
}

For those who are working with function component, here's a basic demo of how you can reset the react select Based on some change/trigger/reduxValue.
import React, { useState, useEffect } from 'react';
import Select from 'react-select';
const customReactSelect = ({ options }) => {
const [selectedValue, setSelectedValue] = useState([]);
/**
* Based on Some conditions you can reset your value
*/
useEffect(() => {
setSelectedValue([])
}, [someReduxStateVariable]);
const handleChange = (selectedVal) => {
setSelectedValue(selectedVal);
};
return (
<Select value={selectedValue} onChange={handleChange} options={options} />
);
};
export default customReactSelect;

in v5, you can actually pass the prop isClearable={true} to make it clearable, which easily resets the selected value

You can set the value to null
const [selectedValue, setSelectedValue] = useState();
const [valueList, setValueList] = useState([]);
const [loadingValueList, setLoadingValueList] = useState(true);
useEffect(() => {
//on page load update valueList and Loading as false
setValueList(list);
loadingValueList(false)
}, []);
const onClear = () => {
setSelectedValue(null); // this will reset the selected value
};
<Select
className="basic-single"
classNamePrefix="select"
value={selectedValue}
isLoading={loadingValueList}
isClearable={true}
isSearchable={true}
name="selectValue"
options={valueList}
onChange={(selectedValue) =>
setSelectedValue(selectedValue)}
/>
<button onClick={onClear}>Clear Value</button>

react-select/creatable.
The question explicitly seeks a solution to react-select/creatable. Please find the below code, a simple answer and solution to the question. You may modify the code for your specific task.
import CreatableSelect from "react-select/creatable";
const TestAction = (props) => {
const { buttonLabelView, className } = props;
const selectInputRef = useRef();
function clearSelected() {
selectInputRef.current.select.select.clearValue();
}
const createOption = (label, dataId) => ({
label,
value: dataId,
});
const Options = ["C1", "C2", "C3", "C4"]?.map((post, id) => {
return createOption(post, id);
});
return (
<div>
<CreatableSelect
ref={selectInputRef}
name="dataN"
id="dataN"
className="selctInputs"
placeholder=" Select..."
isMulti
options={Options}
/>
<button onClick={(e) => clearSelected()}> Clear </button>
</div>
);
};
export default TestAction;

In case it helps anyone, this is my solution: I created a button to clear the selected value by setting state back to it's initial value.
<button onClick={() => this.clearFilters()} >Clear</button>
clearFilters(){
this.setState({ startTime: null })
}
Full code sample below:
import React from "react"
import Select from 'react-select';
const timeSlots = [
{ value: '8:00', label: '8:00' },
{ value: '9:00', label: '9:00' },
{ value: '10:00', label: '10:00' },
]
class Filter extends React.Component {
constructor(){
super();
this.state = {
startTime: null,
}
}
startTime = (selectedTime) => {
this.setState({ startTime: selectedTime });
}
clearFilters(){
this.setState({
startTime: null,
})
}
render(){
const { startTime } = this.state;
return(
<div>
<button onClick={() => this.clearFilters()} >Clear</button>
<Select
value={startTime}
onChange={this.startTime}
options={timeSlots}
placeholder='Start time'
/>
</div>
)
}
}
export default Filter

passing null in value attribute of the react-select will reset it.

if you are using formik then use below code to reset react-select value.
useEffect(()=>{
formik.setFieldValue("stateName", [])
},[])
Where stateName is html field name.
if you want to change value according to another dropdown/select (countryName) then pass that field value in useEffect array like below
useEffect(()=>{
formik.setFieldValue("stateName", [])
},[formik.values.countryName])

Zeeshan's answer is indeed correct - you can use clearValue() but when you do so, the Select instance isn't reset to your defaultValue prop like you might be thinking it will be. clearValue() returns a general Select... label with no data in value.
You probably want to use selectOption() in your reset to explicitly tell react-select what value/label it should reset to. How I wired it up (using Next.js, styled-components and react-select):
import { useState, useRef } from 'react'
import styled from 'styled-components'
import Select from 'react-select'
// Basic button design for reset button
const UIButton = styled.button`
background-color: #fff;
border: none;
border-radius: 0;
color: inherit;
cursor: pointer;
font-weight: 700;
min-width: 250px;
padding: 17px 10px;
text-transform: uppercase;
transition: 0.2s ease-in-out;
&:hover {
background-color: lightgray;
}
`
// Using style object `react-select` library indicates as best practice
const selectStyles = {
control: (provided, state) => ({
...provided,
borderRadius: 0,
fontWeight: 700,
margin: '0 20px 10px 0',
padding: '10px',
textTransform: 'uppercase',
minWidth: '250px'
})
}
export default function Sample() {
// State for my data (assume `data` is valid)
const [ currentData, setCurrentData ] = useState(data.initial)
// Set refs for each select you have (one in this example)
const regionOption = useRef(null)
// Set region options, note how I have `data.initial` set here
// This is so that when my select resets, the data will reset as well
const regionSelectOptions = [
{ value: data.initial, label: 'Select a Region' },
{ value: data.regionOne, label: 'Region One' },
]
// Changes data by receiving event from select form
// We read the event's value and modify currentData accordingly
const handleSelectChange = (e) => {
setCurrentData(e.value)
}
// Reset, notice how you have to pass the selected Option you want to reset
// selectOption is smart enough to read the `value` key in regionSelectOptions
// All you have to do is pass in the array position that contains a value/label obj
// In my case this would return us to `Select a Region...` label with `data.initial` value
const resetData = () => {
regionOption.current.select.selectOption(regionSelectOptions[0])
setCurrentData(data.initial)
}
// notice how my `UIButton` for the reset is separate from my select menu
return(
<>
<h2>Select a region</h2>
<Select
aria-label="Region select menu"
defaultValue={ regionSelectOptions[0] }
onChange={ event => handleDataChange(event) }
options={ regionSelectOptions }
ref={ regionOption }
styles={ selectStyles }
/>
<UIButton
onClick={ resetData }
>
Reset
</UIButton>
</>
)
}

Nether of the solution help me.
This work for me:
import React, { Component, Fragment } from "react";
import Select from "react-select";
import { colourOptions } from "./docs/data";
export default class SingleSelect extends Component {
selectRef = null;
clearValue = () => {
this.selectRef.select.clearValue();
};
render() {
return (
<Fragment>
<Select
ref={ref => {
this.selectRef = ref;
}}
className="basic-single"
classNamePrefix="select"
defaultValue={colourOptions[0]}
name="color"
options={colourOptions}
/>
<button onClick={this.clearValue}>clear</button>
</Fragment>
);
}
}

None of the top suggestions worked for me and they all seemed a bit over the top. Here's the important part of what worked for me
<Select
value={this.state.selected && Object.keys(this.state.selected).length ? this.state.selected : null},
onChange={this.handleSelectChange}
/>

StateManager is abolished now, at least after version 5.5.0.
Now if you use ref, you can just do it like this:
selectRef = null
<Select
...
ref={c => (selectRef=c)}
/>
clearValue = () => {
selectRef.clearValue();
};
Here this c would be the Select2 React Component

This bugged me so here it is:
React select uses arrays so you have to pass an empty array not null.
Using React's useState:
import ReactSelect from 'react-select'
const Example = () => {
const [val, setVal] = useState()
const reset = () => {
setVal([])
}
return <ReactSelect
value={val}/>
}
export default Example

Create a function called onClear, and setSelected to empty string.
Inside the handle submit function, call the onClear function.
This will work perfectly.
Example code:
const onClear = () => {
setSelected("");
};
const handleSubmit = ()=>{
1 your data first ......(what you are posting or updating)
2 onClear();
}

if you are using formik then use below code to reset react-select value.
useEffect(()=>{
formik.setFieldValue("stateName", [])
},[])
Where stateName is html field name.
if you want to change value according to another dropdown/select (countryName) then pass that field value in useEffect array like below
useEffect(()=>{
formik.setFieldValue("stateName", [])
},[formik.values.countryName])

I use redux-observable.
Initial state:
firstSelectData: [],
secondSelectData:[],
secondSelectValue: null
I create an action for filling first select. on change of first select, I call an action to fill second one.
In success of fill first select I set (secondSelectData to [], secondSelectValue to null)
In success of fill second select I set (secondSelectValue to null)
on change of second select, I call an action to update secondSelectValue with the new value selected

Related

How to build a react button that stores the selection in an array

I am trying to create a list of buttons with values that are stored in a state and user is only allowed to use 1 item (I dont want to use radio input because I want to have more control over styling it).
import React from "react";
import { useEffect, useState } from "react";
import "./styles.css";
const items = [
{ id: 1, text: "Easy and Fast" },
{ id: 2, text: "Easy and Cheap" },
{ id: 3, text: "Cheap and Fast" }
];
const App = () => {
const [task, setTask] = useState([]);
const clickTask = (item) => {
setTask([...task, item.id]);
console.log(task);
// how can I make sure only 1 item is added to task
// and remove the other items
// only one option is selectable all the time
};
const chosenTask = (item) => {
if (task.find((v) => v.id === item.id)) {
return true;
}
return false;
};
return (
<div className="App">
{items.map((item) => (
<li key={item.id}>
<label>
<button
type="button"
className={chosenTask(item) ? "chosen" : ""}
onClick={() => clickTask(item)}
onChange={() => clickTask(item)}
/>
<span>{item.text}</span>
</label>
</li>
))}
</div>
);
};
export default App;
https://codesandbox.io/s/react-fiddle-forked-cvhivt?file=/src/App.js
I am trying to only allow 1 item to be added to the state at all the time, but I dont know how to do this?
Example output is to have Easy and Fast in task state and is selected. If user click on Easy and Cheap, select that one and store in task state and remove Easy and Fast. Only 1 item can be in the task state.
import React from "react";
import { useEffect, useState } from "react";
import "./styles.css";
const items = [
{ id: 1, text: "Easy and Fast" },
{ id: 2, text: "Easy and Cheap" },
{ id: 3, text: "Cheap and Fast" }
];
const App = () => {
const [task, setTask] = useState();
const clickTask = (item) => {
setTask(item);
console.log(task);
// how can I make sure only 1 item is added to task
// and remove the other items
// only one option is selectable all the time
};
return (
<div className="App">
{items.map((item) => (
<li key={item.id}>
<label>
<button
type="button"
className={item.id === task?.id ? "chosen" : ""}
onClick={() => clickTask(item)}
onChange={() => clickTask(item)}
/>
<span>{item.text}</span>
</label>
</li>
))}
</div>
);
};
export default App;
Is this what you wanted to do?
Think of your array as a configuration structure. If you add in active props initialised to false, and then pass that into the component you can initialise state with it.
For each task (button) you pass down the id, and active state, along with the text and the handler, and then let the handler in the parent extract the id from the clicked button, and update your state: as you map over the previous state set each task's active prop to true/false depending on whether its id matches the clicked button's id.
For each button you can style it based on whether the active prop is true or false.
If you then need to find the active task use find to locate it in the state tasks array.
const { useState } = React;
function Tasks({ config }) {
const [ tasks, setTasks ] = useState(config);
function handleClick(e) {
const { id } = e.target.dataset;
setTasks(prev => {
// task.id === +id will return either true or false
return prev.map(task => {
return { ...task, active: task.id === +id };
});
});
}
// Find the active task, and return its text
function findSelectedItem() {
const found = tasks.find(task => task.active)
if (found) return found.text;
return 'No active task';
}
return (
<section>
{tasks.map(task => {
return (
<Task
key={task.id}
taskid={task.id}
active={task.active}
text={task.text}
handleClick={handleClick}
/>
);
})};
<p>Selected task is: {findSelectedItem()}</p>
</section>
);
}
function Task(props) {
const {
text,
taskid,
active,
handleClick
} = props;
// Create a style string using a joined array
// to be used by the button
const buttonStyle = [
'taskButton',
active && 'active'
].join(' ');
return (
<button
data-id={taskid}
className={buttonStyle}
type="button"
onClick={handleClick}
>{text}
</button>
);
}
const taskConfig = [
{ id: 1, text: 'Easy and Fast', active: false },
{ id: 2, text: 'Easy and Cheap', active: false },
{ id: 3, text: 'Cheap and Fast', active: false }
];
ReactDOM.render(
<Tasks config={taskConfig} />,
document.getElementById('react')
);
.taskButton { background-color: palegreen; padding: 0.25em 0.4em; }
.taskButton:not(:first-child) { margin-left: 0.25em; }
.taskButton:hover { background-color: lightgreen; cursor: pointer; }
.taskButton.active { background-color: skyblue; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>

Styled qr codes in react select options

I need to create react-select where every option is different styled qr code with js library: qr-code-styling.
The problem is that in documentation qr code are appended to dom elements, and options in react-select are created dynamically. I can't find a way to append these qr codes to my options. I think the problem is that, I can't properly create refs, which I can use to apped the codes. Is there any way to do it?
import React, { useEffect, useRef, createRef } from "react";
import "./styles.css";
import QRCodeStyling from "qr-code-styling";
import Select from "react-select";
const qrCode = new QRCodeStyling({
data: "https://qr-code-styling.com",
width: 300,
height: 300,
image:
"https://upload.wikimedia.org/wikipedia/commons/5/51/Facebook_f_logo_%282019%29.svg",
dotsOptions: {
color: "#4267b2",
type: "rounded"
},
imageOptions: {
crossOrigin: "anonymous",
margin: 20
}
});
const qrCode2 = new QRCodeStyling({
data: "https://qr-code-styling.com",
width: 50,
height: 50,
image:
"https://upload.wikimedia.org/wikipedia/commons/5/51/Facebook_f_logo_%282019%29.svg",
dotsOptions: {
color: "red",
type: "rounded"
},
imageOptions: {
crossOrigin: "anonymous",
margin: 20
}
});
const data = [
{
id: "1",
qrcode: qrCode
},
{
id: "2",
qrcode: qrCode2
}
];
export default function App() {
let refs = useRef([createRef(), createRef()]);
const optionLabel = (option, index) => <div ref={refs.current[option.id]} />;
useEffect(() => {
refs.current.forEach((ref) => qrCode.append(ref.current));
}, []);
return (
<div className="App">
<Select
className="select-logo"
getOptionLabel={optionLabel}
options={data}
/>
</div>
);
}
codesandbox
One solution would be to have the Select let you know when to append the QR codes, by using its onMenuOpen prop. First, though, we need to be able to find the element to append, so we need to add a formatOptionLabel (which is what I suspect you meant to use, instead of getOptionLabel in your sandbox):
<Select
formatOptionLabel={({ id }) => <div id={`qr-code-option-${id}`} />}
...etc
/>
Then, we can add a state variable to be triggered when the menu loads:
const [menuIsOpen, setMenuIsOpen] = React.useState(false)
and, when the menuIsOpen, append the options to their qrCodes:
useEffect(() => {
data.forEach(d => {
let o = document.getElementById('qr-code-option-' + d.id)
if (o) {
d.qrcode?.append(o)
}
})
}, [menuIsOpen])
Finally, we use the onMenuOpen and onMenuClose props to trigger the state changes, resulting in the desired result! The full code of the App component is now:
export default function App() {
const [menuIsOpen, setMenuIsOpen] = React.useState(false)
useEffect(() => {
data.forEach(d => {
let o = document.getElementById(`qr-code-option-${d.id}`)
if (o) {
d.qrcode?.append(o)
}
})
}, [menuIsOpen])
return (
<div className="App">
<Select
className="select-logo"
formatOptionLabel={({ id }) => <div id={`qr-code-option-${id}`} />}
onMenuOpen={() => setMenuIsOpen(true)}
onMenuClose={() => setMenuIsOpen(false)}
options={data}
/>
</div>
);
}
which gives:

toggle effect with react

I'm trying to create a switcher with react that when I click it it change the price of another component and when I click again over it return the original price.
My first approach was this:
I crated the input with the type of a checkbox so when checked is true change the price and when is false return the original price, some kind of toggle all handled with the funcion handleDiscount
<input
checked={toggle}
onChange={handleDiscount}
type="checkbox"
className="switch-input"
/>
I created the handleDiscount function that change the toggle from his initial state which is false to true and and after that a ternary operator that check the condition to set the price.
const handleDiscount = () => {
setToggle(!toggle);
toggle === true ? setPrice(10) : setPrice(20);
};
the problem is that when is click over the checkbox again the price don't change.
I have to work with the useEffect hook? which it's the best approach for this kind of work?
for example I wrote the same code with my knowledge in vanilaJS and work, here is the example:
const switcher = document.querySelector("input");
switcher.addEventListener("click", () => {
const priceBasic = document.querySelector(".priceBasic");
const pricePro = document.querySelector(".pricePro");
const priceMaster = document.querySelector(".priceMaster");
if (switcher.checked == true) {
priceBasic.innerHTML = `<h1>$49.99</h1>`;
pricePro.innerHTML = "$69.99";
priceMaster.innerHTML = "$89.99";
} else {
priceBasic.innerHTML = "$19.99";
pricePro.innerHTML = "$24.99";
priceMaster.innerHTML = "$39.99";
}
});
Issue
The main issue here is that state updates are asynchronous, the toggle value isn't the updated value you just enqueued.
const handleDiscount = () => {
setToggle(!toggle);
toggle === true ? setPrice(10) : setPrice(20);
};
Solution
Use an useEffect to toggle the price state when the toggle state updates.
useEffect(() => {
setPrice(toggle ? 10 : 20);
}, [toggle]);
...
const handleDiscount = () => {
setToggle(toggle => !toggle);
};
Alternatively you can make the checkbox uncontrolled and directly set the price state when the checkbox value updates
const handleDiscount = (e) => {
const { checked } = e.target;
setPrice(checked ? 10 : 20);
};
...
<input
onChange={handleDiscount}
type="checkbox"
className="switch-input"
/>
I am assuming toggle is a state variable and setToggle is s state function, in which case, this won't work:
const handleDiscount = () => {
setToggle(!toggle);
toggle === true ? setPrice(10) : setPrice(20);
};
As setToggle is asynchronous. So toggle does not actually toggle by the time it reaches toggle === true ? setPrice(10) : setPrice(20);
You should instead use useEffect and listen to the change of toggle like so:
useEffect(() => {
toggle === true ? setPrice(10) : setPrice(20);
},[toggle])
And get rid of toggle === true ? setPrice(10) : setPrice(20); inside handleDiscount altogether.
While your specific question of getting the toggle to work has been answered, your example in vanilla javascript hasn't been addressed.
Ideally you wouldn't be hard coding different price options directly into the component, but would be serving an array of products which each held their own specific details including pricing options.
There are various ways of handling this, but in the snippet below your toggle has become a pointer stored in state in individual Product components that can be used to access the appropriate price property of the product object passed to that component. eg.
let product = {
id: 1,
name: 'Product One',
defaultPricePoint: 'red',
pricePoints: {
red: "$49.99",
blue: "$69.99",
}
};
const [pricePoint, setPricePoint] = useState('red');
let price = product.pricePoints[pricePoint];
// "$49.99"
The change handler simply sets the pricePoint state using the value of the input that changed (which were mapped from the keys of the object's prices to begin with). And a useEffect is implemented to set the initial radio selection based on a default set in the product object.
The result is two fairly simple components and decent separation between data and logic.
(note the use of key values that are unique for each mapped element, and not index values)
const App = ({ products }) => {
return (
<div>
{products.map(product =>
<Product key={product.id} product={product} />
)}
</div>
)
}
const Product = ({ product }) => {
const [pricePoint, setPricePoint] = React.useState();
React.useEffect(() => {
setPricePoint(product.defaultPricePoint)
}, [product]);
const handlePricePoint = (e) => {
setPricePoint(e.target.value);
}
return (
<div>
<h3>{product.name}</h3>
<p>{product.pricePoints[pricePoint]}</p>
<form>
{Object.keys(product.pricePoints).map(p =>
<div key={product.id + p}>
<input
type="radio"
name="price-point"
value={p}
onChange={handlePricePoint}
checked={pricePoint === p}
/>
<label for={p}>{p}</label>
</div>
)}
</form>
</div>
);
}
ReactDOM.render(
<App products={products} />,
document.getElementById("root")
);
<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>
<script>
const products = [
{
id: 1,
name: 'Product One',
defaultPricePoint: 'red',
pricePoints: {
red: "$49.99",
blue: "$69.99",
}
},
{
id: 2,
name: 'Product Two',
defaultPricePoint: 'pro',
pricePoints: {
basic: "$19.99",
pro: "$24.99",
master: "$39.99",
}
}
]
</script>
<div id="root"></div>
#codemonkey's answer is correct but if you want something a little simpler:
function YourComponent() {
const [price, setPrice] = useState(20);
const [toggle, setToggle] = useState(false);
const handleDiscount = () => {
const newValue = !toggle;
setToggle(newValue);
setPrice(newValue ? 10 : 20);
}
// ...
}
Or if price is only computed based on toggle:
function YourComponent() {
const [toggle, setToggle] = useState(false);
const price = useMemo(
() => toggle ? 10 : 20,
[toggle]
);
const handleDiscount = () => {
setToggle(!toggle);
}
// ...
}

React Rails Passing props to React Select

I'm trying to pass props from my Rails database to a React Select component using React rails, but the text is appearing invisible within the select option.
View:
= react_component('SelectSearch', options: Course.all.as_json(only: [:title]))
React select component:
import React from 'react';
import Select from 'react-select';
class SelectSearch extends React.Component {
constructor(props) {
super(props)
}
state = {
selectedOption: null,
};
handleChange = selectedOption => {
this.setState({ selectedOption });
console.log(`Option selected:`, selectedOption);
};
render() {
const { selectedOption } = this.state;
return (
<Select
value={selectedOption}
onChange={this.handleChange}
options={this.props.options}
/>
);
}
}
export default SelectSearch;
When something in the select is clicked, it console logs, for example:
Option selected: {title: "English"}
But opening the select, its completely blank. There is obviously an option there that can be clicked, but nothing is displayed. Likewise for searching, no options are displayed.
I know this is because I'm passing the props incorrectly, or handling the data incorrectly, I don't want {title: "English"} I just want English but not sure how to filter this correctly.
See the react-select example:
const options = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' },
{ value: 'vanilla', label: 'Vanilla' }
]
const MyComponent = () => (
<Select options={options} />
)
Your options contains unneeded 'title' attribute and not contains 'value' and 'label'. Fix it.
Took me a while to figure it out, but got this working:
= react_component('SelectSearch', data: Course.all.as_json(only: [:title]))
import React from 'react';
import Select from 'react-select';
class SelectSearch extends React.Component {
constructor(props) {
super(props);
}
state = {
selectedOption: null,
};
handleChange = selectedOption => {
this.setState({ selectedOption });
console.log(`Option selected:`, selectedOption);
};
render() {
const { selectedOption } = this.state;
return (
<Select
value={selectedOption}
onChange={this.handleChange}
options={this.props.data}
getOptionLabel={(option) => option.title}
getOptionValue={(option) => option.title}
/>
);
}
}
export default SelectSearch;
React-select expects a value and a label and this seems to be the easiest way to pass it.

Formik & yup form validation not working as expected with VirtualizedSelect

I created a form with formik in order to have form validations.
I have used the components Formik, Form, Field form formik and configured them:
import { Formik, Form, Field } from "formik";
import { object, string } from "yup";
import isEmpty from "lodash/isEmpty";
import FormikSelectInput from "../common/FormikSelectInput";
class App extends Component {
render() {
const options = this.props.categories.map(c => {
return { label: c.name, value: c.name };
});
return (
<Formik
validationSchema={object().shape({
category: string().required("Category is required.")
})}
initialValues={this.props.obj}
onSubmit={(values, actions) => {
console.log(values);
}}
render={({ errors, dirty, isSubmitting, setFieldValue }) => (
<Form>
<Field
name="category"
label="Categories"
value={this.props.obj.category.name}
options={options}
component={FormikSelectInput}
/>
<button
type="submit"
className="btn btn-default"
disabled={isSubmitting || !isEmpty(errors) || !dirty}
>
Save
</button>
</Form>
)}
/>
);
}
}
//Prop Types validation
App.propTypes = {
obj: PropTypes.object.isRequired,
categories: PropTypes.array.isRequired,
actions: PropTypes.object.isRequired
};
const getElementByID = (items, id) => {
let res = items.filter(l => l.id === id);
return res.length ? res[0] : null; //since filter returns an array we need to check for res.length
};
//Redux connect
const mapStateToProps = ({ objects, categories }, ownProps) => {
let obj = {
id: "",
name: "",
category: { id: "", name: "" }
};
return {
obj: getElementByID(objects, ownProps.match.params.id) || obj,
categories: categories
};
};
export default connect(
mapStateToProps,
{...}
)(App);
And I have a custom component 'FormikSelectInput':
import React, { Component } from "react";
import classnames from "classnames";
import VirtualizedSelect from "react-virtualized-select";
import "react-select/dist/react-select.css";
import "react-virtualized/styles.css";
import "react-virtualized-select/styles.css";
const InputFeedback = ({ children }) => (
<span className="text-danger">{children}</span>
);
const Label = ({ error, children, ...props }) => {
return <label {...props}>{children}</label>;
};
class FormikSelectInput extends Component {
constructor(props) {
super(props);
this.state = { selectValue: this.props.value };
}
render() {
const {
field: { name, ...field }, // { name, value, onChange, onBlur }
form: { touched, errors }, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
className,
label,
...props
} = this.props;
const error = errors[name];
const touch = touched[name];
const classes = classnames(
"form-group",
{
"animated shake error": !!error
},
className
);
console.log("props", props);
return (
<div className={classes}>
<Label htmlFor={name} error={error}>
{label}
</Label>
<VirtualizedSelect
name={name}
id={name}
className="form-control"
{...field}
{...props}
onChange={(selectValue) => this.setState(() => {
this.props.form.setFieldValue('category',selectValue)
return { selectValue }
})}
value={this.state.selectValue}
/>
{touch && error && <InputFeedback>{error}</InputFeedback>}
</div>
);
}
}
export default FormikSelectInput;
My component is working and I am able to select an option, but why formik together with 'yup' validation showing me an error when I empty the select field.
When I clear my select field I get an ERROR - 'category must be a string type, but the final value was: null. If "null" is intended as an empty value be sure to mark the schema as .nullable()'
My code is based on the this example.
It looks like the field is expecting the string to be required based on your validationSchema.
The error helped point me in the right direction. Here's the docs for Yup .nullable(): https://github.com/jquense/yup#mixednullableisnullable-boolean--true-schema
Try adding .nullable() to the chain of validations.
validationSchema={object().shape({
category: string().required("Category is required.").nullable()
})}
Hope this helps.
Don't assign default value (initialValue in formik) as null.
I have also had same problem like I was using initial values as this way
const initValues = {
name : data?.name
}
so , my problem was till the data?.name was defined every thing was fine but since the data?.name was null then it is assigned null to the initValues of formik but in yup I haven't assign it to be null so I did it like
const initValues = {
name : data?.name || ""
}
It checks either data?.name is null if null then it will assign them to ''.So it wont be nullable

Categories

Resources