Display text according to the value of react select - reactjs

I would like to display a different text according to the value selected in my react-select. handlechange time is working fine, my problem is when I try to retrieve the value in my p tag to display different text depending on the value
My code:
const timeSlots = [
{
value: 0,
label: 'what is your choice ?'
},
{
value: 1,
label: 'choice 1'
},
{
value: 2,
label: 'choice 2'
},
{
value: 3,
label: 'choice 3'
},
{
value: 4,
label: 'choice 4'
}
]
class WidgetBooking extends Component {
state ={
selectedOption: null,
}
handleChangeTime = (selectedOption) => {
this.setState({ selectedOption });
console.log(selectedOption);
}
render() {
const { selectedOption } = this.state;
return (
<>
<div className="time-slots padding-bottom-30px">
<Select
value={selectedOption}
onChange={this.handleChangeTime}
placeholder="what is your choice ?"
options={timeSlots}
/>
</div>
{selectedOption === '1' &&
<p className="d-block widget-title">756, 23 $</p>
}
{selectedOption === '2' &&
<p className="d-block widget-title">865, 23 $</p>
}
{selectedOption === '3' &&
<p className="d-block widget-title">756, 23 $</p>
}
</div>
</>
);
}
}
I would like to display a different price in a p tag, like show above.
Thanks for your help

You've got two issues here:
confusion between an option and the option's value
confusion between string and number
Option vs. Value
What do you see when you call console.log(selectedOption) inside handleChangeTime? I see a complete option object that looks like {value: 2, label: "choice 2"}. That's what you are saving to this.state.selectedOption. But when you compare it in your render, you are expecting this.state.selectedOption to be a numeric string like '1' or '2' -- not an object.
Either one is fine to setState with, but you have to be consistent about what it is or else you will never have a match. If you want to keep handleChangeTime the same, then your render checks need to look like selectedOption?.value === 1. If you want to keep your render() the same, then you need to be setting the state like this.setState({ selectedOption: selectedOption.value.toString() }). That toString() brings us to the next issue.
Number vs. String
When you are comparing values, the number 1 and the string '1' are not the same. If you were dealing with the DOM select element directly, you would likely wind up with string values. But the react-select package calls onChange with the option in the same format that you provided it. In this case, your value property is a number. Again it's consistency that's important. I recommend that you keep it a number everywhere and change your render conditions from selectedOption === '1' to selectedOption === 1.
Corrected Code:
import React, { Component } from "react";
import Select from "react-select";
const timeSlots = [ /* ... */ ];
class WidgetBooking extends Component {
state = {
selectedOption: null // will be a number or null
};
handleChangeTime = ({ value }) => { // destructure to get just the value
this.setState({ selectedOption: value });
};
render() {
const { selectedOption } = this.state;
return (
<>
<div className="time-slots padding-bottom-30px">
<Select
value={selectedOption}
onChange={this.handleChangeTime}
placeholder="what is your choice ?"
options={timeSlots}
/>
</div>
{selectedOption === 1 && (
<p className="d-block widget-title">756, 23 $</p>
)}
{selectedOption === 2 && (
<p className="d-block widget-title">865, 23 $</p>
)}
{selectedOption === 3 && (
<p className="d-block widget-title">756, 23 $</p>
)}
</>
);
}
}

Related

Explain item.id === id ? { ...item, checked: !item.checked } : item

const listItems = items.map((item) =>
item.id === id ? { ...item, checked: !item.checked } : item
)
Especially what is the difference between item.id and id? and why use : item at the end?
Full code below, Note that the code isn't something I'm working on, it is a tutorial and there is still more to code
import React from "react";
import { useState } from "react";
import { FaTrashAlt } from "react-icons/fa";
const Content = () => {
const [items, setItems] = useState([
{
id: 1,
checked: false,
item: "One half bag of cocoa covered",
},
{
id: 2,
checked: false,
item: "Item 2",
},
{
id: 3,
checked: false,
item: "Item 3",
},
]);
const handleCheck = (id) => {
console.log(`key: ${id}`);
const listItems = items.map((item) =>
item.id === id ? { ...item, checked: !item.checked } : item
);
setItems(listItems);
localStorage.setItem("shoppinglist", JSON.stringify(listItems));
};
return (
<main>
<main>
<ul>
{items.map((item) => (
<li className="item" key={item.id}>
<input
type="checkbox"
onChange={() => handleCheck(item.id)}
checked={item.checked}
/>
<label
style={item.checked ? { color: "red" } : { color: "green" }}
onClick={() => handleCheck(item.id)}
>
{item.item}
</label>
{/* <button>Delete</button> */}
<FaTrashAlt
onClick={() => handleCheck(item.id)}
role="button"
tabIndex="0"
/>
</li>
))}
</ul>
</main>
</main>
);
};
export default Content;
items.map(item => ) is mapping over an array of items, item is the object of the current item being mapped
id is the id that comes from handleCheck function(triggered onClick), which is the id of the clicked item, while item.id is the id of the current item from the array of objects
item.id === id ? { ...item, checked: !item.checked } : item uses the ternary operator which is a shorthand if/else to check if the id of the current item is the same as the id of the clicked item
if it is(true) it adds to the current item object checked: !item.checked, if it isn't(false) it returns the item object as it is
Here this code trying to keep items array immutable,if you change item.checked value it without using
It will modify items array too
{ ...item, checked: !item.checked }
And Id is used to collect only particular item and modifications of it.
The map function iterates through all the items in a list. Each item has an id, which is the list.id variable. The function takes id as a param.
Then if item.id equals the passed in id, a modified item will be returned. If not, the iten is returned unmodified. That's what :item at the end is for.
A few basics to start since I'm not sure where the confusion lies:
map() takes the contents of a list and returns another list / array
based on those concepts. e.g. [1,2,3].map(x => x *2) will return [ 2, 4, 6 ]
The ternary ?: operator takes three args (hence the name) If the
first is true, returns the second, otherwise the third
e.g. true ? 'yes' : 'no' returns 'yes'
The spread operator populates one object with the contents of
another. e.g. If a = { foo: 'Hello' } then
{ ...a, bar: 'World' } means { foo: 'Hello', bar: 'World' }
So your example takes the first list and If the ID matches the checkbox ID being targetted, it returns a new object with the state flipped, otherwise it returns the original object.
The net effect is a list of IDs with the "checked" status flipped on one of the items. Make sense?

In ReactJS how do I keep track of the state of a group of checkboxes?

I am trying to keep track of which boxes are checked in my local state(you can check multiple boxes). I want to be able to check and uncheck the boxes and keep track of the ids of the boxes that are checked. I will do something with the values later. This is what I have so far:
import React, { Component } from 'react'
import './App.css'
import CheckBox from './CheckBox'
class App extends Component {
constructor(props) {
super(props)
this.state = {
fruits: [
{id: 1, value: "banana", isChecked: false},
{id: 2, value: "apple", isChecked: false},
{id: 3, value: "mango", isChecked: false},
{id: 4, value: "grape", isChecked: false}
],
fruitIds: []
}
}
handleCheckChildElement = (e) => {
const index = this.state.fruits.findIndex((fruit) => fruit.value === e.target.value),
fruits = [...this.state.fruits],
checkedOrNot = e.target.checked === true ? true : false;
fruits[index] = {id: fruits[index].id, value: fruits[index].value, isChecked: checkedOrNot};
this.setState({fruits});
this.updateCheckedIds(e);
}
updateCheckedIds = (e) => {
const fruitIds = [...this.state.fruitIds],
updatedFruitIds= fruitIds.concat(e.target.id);
this.setState({updatedFruitIds});
}
render() {
const { fruits } = this.state;
if (!fruits) return;
const fruitOptions = fruits.map((fruit, index) => {
return (
<CheckBox key={index}
handleCheckChildElement={this.handleCheckChildElement}
isChecked={fruit.isChecked}
id={fruit.id}
value={fruit.value}
/>
);
})
return (
<div className="App">
<h1>Choose one or more fruits</h1>
<ul>
{ fruitOptions }
</ul>
</div>
);
}
}
export default App
So basically I am able to check and uncheck the boxes, but I cannot seem to update and store the fruitIds. Here is my checkbox component also:
import React from 'react'
export const CheckBox = props => {
return (
<li>
<input key={props.id}
onChange={props.handleCheckChildElement}
type="checkbox"
id={props.id}
checked={props.isChecked}
value={props.value}
/>
{props.value}
</li>
)
}
export default CheckBox
Also if you have a cleaner ways to do this than the way I am doing it, I would love to see it.
This is what if I were to approach it I will do. I will create a one dimensional array that holds the id's of the fruits when A fruit if clicked(checked) I will add it id to the array and when its clicked the second time I check if the array already has the id I remove it. then the presence of id in the array will mean the fruit is checked otherwise its not checked So I will do something like below
this.state={
fruitsIds: []
}
handleCheckChildElement=(id) => {
//the logic here is to remove the id if its already exist else add it. and set it back to state
const fruitsIds = this.state.fruitsIds;
this.setState({fruitsIds: fruitsIds.contains(id) ? fruitsIds.filter(i => i != id) : [...fruitsIds, id] })
}
then I render the checkboxes like
<CheckBox key={index}
handleCheckChildElement={this.handleCheckChildElement}
isChecked = { this.state.fruitsIds.contains(fruit.id)}
id={fruit.id}
/>
This is because you can always use the id to get all the other properties of the fruit so there is absolutely no need storing them again.
then the checkbox component should be as follows
export const CheckBox = props => {
return (
<li>
<input key={props.id}
onChange={() => props.handleCheckChildElement(props.id)}
type="checkbox"
id={props.id}
checked={props.isChecked}
value={props.value}
/>
{props.value}
</li>
)
}
The reason you are not getting your ids updated because:
You are trying to concat a non array element to an array.
concat is used for joining two or more arrays.
updatedFruitIds = fruitIds.concat(e.target.id);
You are not updating your actual fruitIds state field. I dont know why you are using "updatedFruitIds" this variable but due to above error it will always result into a single element array.
this.setState({ updatedFruitIds });
updateCheckedIds = e => {
const fruitIds = [...this.state.fruitIds],
updatedFruitIds = fruitIds.concat([e.target.id]);
this.setState({ fruitIds: updatedFruitIds });
};
OR
updateCheckedIds = e => {
const fruitIds = [...this.state.fruitIds, e.target.id],
this.setState({ fruitIds });
};

when using {react-select} Cannot read property 'name' of undefined

I am very beginning to reactJS and front end
I added react-select npm for my dropdown like below, before added react-select everything is working fine. How to define name in Select?
<div className="container">
<div className="row">
<div className="col-md-4" />
<div className="col-md-4">
<Select
options={this.state.allGenres}
onChange={this.props.onDataChange}
name="genre"
/>
</div>
<div className="col-md-4" />
</div>
</div>
this is my array,
var Data = response.data;
const map = Data.map((arrElement, index) => ({
label: arrElement,
value: index
}));
example:
[
{
"label": "Action",
"value": 0
},
{
"label": "Comedy",
"value": 1
},
{
"label": "Documentary",
"value": 2
}
]
error message coming in here,
dataChange(ev, action) {
this.setState({
[ev.target.name]: ev.target.value
});
}
render()
render() {
return (
<Movie
onPostData={this.postData.bind(this)}
onDataChange={this.dataChange.bind(this)}
/>
);
}
Error
Uncaught TypeError: Cannot read property 'name' of undefined
at Movies.dataChange
You expect the first argument in react-select´s onChange method to be an event object, but it isn't.
The first argument is the selected option (or options if you have isMulti set).
There is also a second argument which is an object with the following attributes:
action: The action which triggered the change
name: The name given to the Select component using the name prop.
So if you want to use the name:
onDataChange={(value, action) => {
this.setState({
[action.name]: value
})
}}
Reference in source code
I worked around this method and it worked.
handleSelectChange: function(name) {
return function(newValue) {
// perform change on this.state for name and newValue
}.bind(this);
},
render: function() {
return (
<div>
<Select ...attrs... onChange={this.handleSelectChange('first')} />
<Select ...attrs... onChange={this.handleSelectChange('second')} />
</div>);
}
This is how you can get the value(s) of the selected option(s) and the name of the input as well.
For more info, check this issue on Github.
handleChange = (selectedOptions, actionMeta) => {
const inputName = actionMeta.name;
let selectedValues;
if (Array.isArray(selectedOptions)) {
// An array containing values of the selected options (like: ["one", "two", "three"]
selectedValues = selectedOptions.map(option => option.value);
// OR, use this if you want the values of the selected options as a string separated by comma (like: "one,two,three")
selectedValues = selectedOptions.map(option => option.value).join(",");
} else {
// An array containing the selected option (like: ["one"])
selectedValues = [selectedOptions.value];
// OR, use this if you want the value of the selected option as a string (like: "one")
selectedValues = selectedOptions.value;
}
// Do whatever you want with the selected values
//..
this.setState({
[inputName]: selectedValues
});
}
<Select
name="genre"
options={this.state.allGenres}
onChange={this.handleChange}
/>

react-select how to make a pre selected fixed options

I want to make a pre selected options that cannot be deleted based on w. whether a client has been visited or no, here is what I want to achieve
const { clients } = this.props.clients;
const listOfClients =
clients !== null &&
clients.clients.map(client => ({
value: client._id,
label: client.company
? client.company
: client.lastname + " " + client.lastname,
last_visit: client.last_visit,
wilaya: client.wilaya,
visited: client.visited // true : false
}));
and that's how i render my select options
<Select
name="clients"
isMulti
value={this.state.clients}
onChange={e => this.onChange(e, "clients")}
isClearable={this.state.clients.some(client => !client.visited)}
options={listOfClients || []}
className="basic-multi-select"
classNamePrefix="select"
/>
and my state holds an array of clients as follows :
[{value: "5c0e784f0249ea83d88bddf3", label: "sarl medic", visited: true}]
if visited = true , then this selected option must be greyed out and cannot be deleted. I've looked up this example but i don't understand where i went wrong. Thank you :)
Your listOfClients options are missing the important isFixed, it should be the same value as visited if I understand your code correctly.
Also with multi select you will need to disable manually the remove function like the following code:
const listOfClients =
clients !== null &&
clients.map(client => ({
value: client._id,
label: client.company
? client.company
: client.lastname + " " + client.lastname,
last_visit: client.last_visit,
wilaya: client.wilaya,
visited: client.visited,
isFixed: client.visited // true : false
}));
class App extends Component {
constructor(props) {
super(props);
this.state = {
clients: []
};
}
onChange = (e, option) => {
if (option.removedValue && option.removedValue.isFixed) return;
this.setState({
clients: e
});
};
render() {
return (
<div className="App">
<Select
name="clients"
isMulti
value={this.state.clients}
onChange={this.onChange}
isClearable={!this.state.clients.some(client => client.visited)}
options={listOfClients || []}
className="basic-multi-select"
classNamePrefix="select"
styles={styles}
/>
</div>
);
}
}
Here a live example.

Select React Component data option not appeard

I am facing an issue with representing data inside select-react Component, I had successfully getting data form server side (node js ) by componentDidMount()
componentDidMount(){
fetch('api/transporationTypes')
.then( res => res.json())
.then(trasnportation => this.setState({trasnportation }, () => console.log(trasnportation)));
}
but I cannot set loaded data inside React-Select Component I tried the below here below how this component works with static data.
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' },
]}
/>
);
}
}
but when I trying to load dynamic data with code below it represent No results Found see screenshot: http://prntscr.com/jqvp76.
alos printing the data via console.log('optionItems',optionItems) in dev tools print correctly http://prntscr.com/jqvq7v
How can I make option of select component works successfully
render() {
const { selectedOption } = this.state.selectedOption;
let optionItems = this.state.trasnportation.map((trans) =>
[ {value: `${trans.TransportationType}` , label : `${trans.TransportationType}`}]
);
console.log('optionItems',optionItems)
return (
<div className="row">
<h1>Choose Tranportation Type</h1>
<Select className="col-md-8"
name="form-field-name"
value={selectedOption}
onChange={this.handleChange1}
option={optionItems}
placeholder = "Select one of below"/>
</div>
);
}
}
Thanks -- Fadi
The items have wrong type:
let optionItems = this.state.trasnportation.map((trans) =>
[ {value: `${trans.TransportationType}` , label : `${trans.TransportationType}`}]
);
to
let optionItems = this.state.trasnportation.map((trans) =>
({value: `${trans.TransportationType}` , label : `${trans.TransportationType}`})
); // [] -> ()
It likely has something to do with the typos, in the code supplied there is:
trasnportation
transporation
transportation

Resources