Whenever I change the value in my select, I have to show different graphs
if I choose bar, it should display bar chart
if I choose line, it should display line chart
Initially the value is zero so it displays bar chart, then when I change to line it works fine, but when I go back to bar it does not.
Code:
const [chart,setChart]=useState(0)
const [filterData, setFilterData] = useState([])
export const ChartTypes =[
{
value: 0,
label: 'Bar'
},
{
value: 1,
label: 'Line'
},
{
value: 2,
label: 'Scatter Plot'
},
// My select Component
const handleChartChange = (event) =>{
setChart(event.target.value)
}
<FormControl variant="filled" className={classes.formControl}>
<InputLabel htmlFor="filled-age-native-simple">Chart</InputLabel>
<Select
native
value={chart}
onChange={handleChartChange}
inputProps={{
name: 'Chart',
id: 'filled-age-native-simple',
}}
>
{ChartTypes.map((e,chart)=>{
return (
<option value={e.value} key={e}>
{e.label}
</option>
)
})}
</Select>
{/* </Col>
<Col> */}
</FormControl>
// conditional rendering the component
{chart === 0 ? <BarChart
graphData={filterData}
filterType={graphFilter}
/> : <LineChart
graphData={filterData}
filterType={graphFilter} />
}
Edit
Thanks, it worked with the support of the below answers
Your issue is that the value of event.target.value is going to be a string "0" instead of a number 0 which you check for in your chart === 0 check.
Your initial value works because you hard-coded a zero as a number.
Option 1
You can either change the check to not include the type by doing chart == 0
OR
Option 2
You can change the value in your ChartTypes array to a string:
export const ChartTypes = [
{
value: '0',
label: 'Bar'
},
{
value: '1',
label: 'Line'
},
{
value: '2',
label: 'Scatter Plot'
}
];
and make your initial value const [chart, setChart] = useState('0')
OR
Option 3
You can change your handleChartChange function to parse the value as a number:
const handleChartChange = (event) => {
setChart(parseInt(event.target.value));
}
try this way, it works for me.
//define the state in this way:
const [state,setSate]=useState({chart:0})
const handleChartChange = (e) => {
setState({...state, chart:e.target.value})
}
<Select
...
value={state.chart}
onChange={handleChartChange}
...
>
</Select>
Related
I have a reuseable component i created and the value is undefined. I console logged the currentTarget in the Select.tsx and it returns the value correctly. However, the actual component using the select is the one that returns an undefined value. What am i missing here?
This is the code in the select.Tsx
export const Select = (props: any) => {
const [data] = useState(props.data);
const [selectedData, updateSelectedData] = useState('');
function handleChange(event: any) {
updateSelectedData(event.currentTarget.value);
console.log(event.currentTarget.value, 'in select.tsx line 10');
if (props.onSelectChange) props.onSelectChange(selectedData);
}
let options = data.map((data: any) => (
<option key={data.id} value={data.id}>
{data.label}
</option>
));
return (
<>
<select
className={props.className ? props.className : 'float-right rounded-lg w-[50%] '}
onChange={handleChange}>
<option>Select Item</option>
{options}
</select>
</>
);
};
This is the code being used in the actual component...
...
const actionSelectOptions = [
{ id: 1, label: 'Pricing Revised', value: 'Pricing Revised' },
{ id: 2, label: 'Cost Build-up Posted', value: 'Cost Build-up Posted' },
{ id: 3, label: 'Pricing Created', value: 'Pricing Created' },
];
function onSelectChange(event: any) {
console.log(event.currentTarget.value);
}
return (
...
<Select
className="flex justify-center items-center rounded-lg"
data={actionSelectOptions}
onSelectChange={onSelectChange}
/>
...
)
I tried changing between target and currenTarget in the main component. It both get undefined.. the console works in the select component it seems as if the data is not passing on as its suppose to.
I also tried writing an arrow function within the actual called component for example:
<Select
...
onSelectChange={(e)=> console.log(event.currentTarget)}
I have a set of select menus and I need to find out the values for all of them when I reset them using reset buttons.
The problem is this only works on change event for options and I can't make it work on reset buttons on the first click as it doesn't detect the change.
Code sample here:
https://stackblitz.com/edit/react-rmp8kf
import React from 'react';
import { useState } from 'react';
import uuid from 'react-uuid';
export default function Select() {
const [value, setValue] = useState({
select1: '',
select2: '',
});
const selectOptions = [
{
options: [
{
text: 'All',
value: '0',
},
{
text: 'Blue',
value: '1',
},
{
text: 'Yellow',
value: '2',
},
{
text: 'Green',
value: '3',
},
],
},
{
options: [
{
text: 'All',
value: '0',
},
{
text: 'Black',
value: '1',
},
{
text: 'White',
value: '2',
},
],
},
];
const handleOnChange = (e) => {
const valueSelected = e.target.value;
setValue({
...value,
[e.target.name]: valueSelected,
});
printAllSelectValues();
};
const resetAllSelections = (e) => {
e.preventDefault();
setValue({
select1: '',
select2: '',
});
printAllSelectValues();
};
const resetSelection = (e) => {
e.preventDefault();
setValue({
...value,
[e.target.dataset.selectName]: '',
});
printAllSelectValues();
};
const printAllSelectValues = () => {
const selectMenus = document.querySelectorAll('select');
selectMenus.forEach((select) =>
console.log(select.name + '=' + select.value)
);
};
return (
<form>
<div>
<label>
<select
name="select1"
value={value.select1}
onChange={handleOnChange}
>
{selectOptions[0].options.map((option) => (
<option value={option.value} key={uuid()}>
{option.text}
</option>
))}
</select>
</label>
<button data-select-name="select1" onClick={resetSelection}>
Reset
</button>
</div>
<div>
<label>
<select
name="select2"
value={value.select2}
onChange={handleOnChange}
>
{selectOptions[1].options.map((option) => (
<option value={option.value} key={uuid()}>
{option.text}
</option>
))}
</select>
</label>
<button data-select-name="select2" onClick={resetSelection}>
Reset
</button>
</div>
<button onClick={resetAllSelections}>Reset All</button>
</form>
);
}
The stackblitz sandbox you linked seems to do exactly what you need to. You are getting both values correctly when:
You select any value in any of the 2 select elements
You reset any of the select values individually by using the reset button on the side of each select element
You reset both values at once by using the "Reset All" button
What is the problem? Do you want to get the updated values ?
UPDATE
Okey. So react has the nature that every state update is async, meaning that you need to wait for a state update to happen to use its updated values. Now, you cannot use promises or async/await to do this because react is built intentionally this way and gives you the tools to do so. So you need to use the useEffect hook for this.
https://stackblitz.com/edit/react-bekzpz
Note: worth mentioning that you don't need to grab the data from the HTML elements but in case you want to manipulate DOM elements in react, you are not supposed to do it using the document but by using the useRef hook
I am new to React. I'm using react-select and I've used the following code. The dropdown is displayed but I'm unable to see names and unable to view after selecting.
<Select
variant="outlined"
margin="normal"
fullWidth
value={this.state.selected}
options={RewardAutomationsList}
name="selected"
onChange={this.handleChange}
placeholder='None'
>
{RewardAutomationsList.map((option) => (
<option key={option.id} value ={option.name} label={option.name}>
{option.name}
</option>
))}
</Select>
handleChange = event => {
this.setState({
selected: event.name
});
};
The RewardAutomationsList looks like this:
RewardAutomationsList:
0:{name: "TEST 1 (INR 100)", id: "123"}
1:{name: "test 2 (INR 250)", id: "456"}
Can someone help with this?
same npm package use like this block code.
import React, { Component } from 'react'
import Select from 'react-select'
const options = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' },
{ value: 'vanilla', label: 'Vanilla' }
]
const MyComponent = () => (
<Select options={options} />
)
react-select accepts an array of objects having label and value keys. Your option objects in RewardAutomationsList have id and name keys, so it can't be displayed. You need to change them.
Also, when you subscribe to change events with react-select's onChange prop, the callback function you provide receives the selectedOption, not the event.
The following should work:
const RewardAutomationsList = [
{ label: "TEST 1 (INR 100)", value: "123" },
{ label: "test 2 (INR 250)", value: "456" },
];
class App extends React.Component {
state = {
selected: null,
}
handleChange = (selectedOption) => {
this.setState({
selected: selectedOption,
});
};
render() {
return (
<React.Fragment>
<Select
fullWidth
margin="normal"
name="selected"
onChange={this.handleChange}
options={RewardAutomationsList}
placeholder="None"
value={this.state.selected}
variant="outlined"
/>
{/* It's not necessary and it's only here to show the current state */}
<pre>{JSON.stringify(this.state, null, 2)}</pre>
</React.Fragment>
);
}
}
I'm new to react and trying to learn on my own. I started using react-select to create a dropdown on a form and now I'm trying to pass the value of the option selected. My state looks like this.
this.state = {
part_id: "",
failure: ""
};
Then in my render
const {
part_id,
failure
} = this.state;
My form looks has 2 fields
<FormGroup>
<Label for="failure">Failure</Label>
<Input
type="text"
name="failure"
placeholder="Failure"
value={failure}
onChange={this.changeHandler}
required
/>
</FormGroup>
<FormGroup>
<Label for="part_id">Part</Label>
<Select
name="part_id"
value={part_id}
onChange={this.changeHandler}
options={option}
/>
</FormGroup>
the changeHandler looks like this
changeHandler = e => {
this.setState({ [e.target.name]: e.target.value });
};
The change handler works fine for the input but the Select throws error saying cannot read property name. I went through the API docs and came up with something like this for the Select onChange
onChange={part_id => this.setState({ part_id })}
which sets the part_id as a label, value pair. Is there a way to get just the value? and also how would I implement the same with multiselect?
The return of react-select onChange event and the value props both have the type as below
event / value:
null | {value: string, label: string} | Array<{value: string, label: string}>
So what the error means is that you can't find an attribute of null (not selected), or any attributes naming as name (you need value or label)
For multiple selections, it returns the sub-list of options.
You can find the related info in their document
const options = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' },
{ value: 'vanilla', label: 'Vanilla' },
];
Update
For your situation (single selection)
option having type as above
const option = [
{value: '1', label: 'name1'},
{value: '2', label: 'name2'}
]
state save selected value as id
changeHandler = e => {
this.setState({ part_id: e ? e.value : '' });
};
pick selected option item via saved id
<Select
name="part_id"
value={option.find(item => item.value === part_id)}
onChange={this.changeHandler}
options={option}
/>
For multiple selections
state save as id array
changeHandler = e => {
this.setState({ part_id: e ? e.map(x => x.value) : [] });
};
pick via filter
<Select
isMulti // Add this props with value true
name="part_id"
value={option.filter(item => part_id.includes(item.value))}
onChange={this.changeHandler}
options={option}
/>
onChange function is a bit different in react-select
It passes array of selected values, you may get first one like
onChange={([selected]) => {
// React Select return object instead of value for selection
// return { value: selected };
setValue(selected)
}}
I have tried the above solutions but some of these solutions does update the state but it doesn't gets rendered on the Select value instantly.
Herewith a demo example:
this.state = {
part_id: null,
};
handleUpdate = (part_id) => {
this.setState({ part_id: part_id.value }, () =>
console.log(`Option selected:`, this.state.part_id)
);
};
const priceOptions = [
{ value: '999', label: 'Item One' },
{ value: '32.5', label: 'Item Two' },
{ value: '478', label: 'Item Three' }
]
<Select
onChange={this.handleUpdate}
value={priceOptions.find(item => item.value === part_id)}
options={priceOptions}
placeholder={<div>Select option</div>}
/>
I have two logically dependent HTML select components.
The first one represents Districts list and the second one represents corresponding Subdistricts.
When a District is selected, the Subdistricts option array should be altered to represent Subdistricts from the selected District.
Here is how they are represented in components render method:
<div style={{display: "inline-block", marginRight: "20px"}}>
<h style={{fontSize: "20px"}}>District</h>
<br/>
<select id="districts-select-list" onChange={this.updateDistrictData}>
{this.state.districtsSelectOptionsArrayState}
</select>
</div>
<div style={{display: "inline-block"}}>
<h style={{fontSize: "20px"}}>Subdistrict</h>
<br/>
<select id="subdistricts-select-list">
{this.state.subdistrictsSelectOptionsArrayState}
</select>
{this.state.subdistrictsSelectOptionsArrayState}
</div>
As you see options are state dependent.
Here is how update the data:
updateDistrictData(e) {
this.setState({subdistrictsSelectOptionsArrayState : []});
var categoryList = document.getElementById("districts-select-list");
var selectedDistrictId = categoryList.options[categoryList.selectedIndex].value;
if(selectedDistrictId == undefined) {
return;
}
var currentSubdistrictList = this.subdistrictDataArray[selectedDistrictId];
if(currentSubdistrictList != undefined) {
var currentSubdistrictListLength = currentSubdistrictList.length;
if(
currentSubdistrictListLength == undefined ||
currentSubdistrictListLength == 0
) {
return;
}
for(var index = 0; index < currentSubdistrictListLength; index++) {
var currentDistrictObject = currentSubdistrictList[index];
if(currentDistrictObject != undefined) {
var currentSubdistrictId = currentDistrictObject["id"];
var currentSubdistrictName = currentDistrictObject["name"];
console.log("SUBDISTRICT NAME IS : " + currentSubdistrictName);
var currentSubdistrictOption = (
<option value={currentSubdistrictId}>
{currentSubdistrictName}
</option>
);
this.setState(prevState => ({
subdistrictsSelectOptionsArrayState:[
...prevState.subdistrictsSelectOptionsArrayState,
(
<option value={currentSubdistrictId}>
{currentSubdistrictName}
</option>
)
]
}));
}
}
}
}
I call updateDistrictData method after retrieving subdistricts from server and in District select component's onChange method.
When the page is loaded for the first time, the districts and corresponding subdistricts are altered correctly.
But when I change district afterwards using the District select component itself, the subdistricts select component is populated with repetous subdistrict option repeated as many times as the number of subdisctricts in the current district.
What am I doing wrong?
The problem is caused by the use of vars (currentSubdistrictId and currentSubdistrictName) in a closure (the setState callBack).
=> because of the var declaration, the last value taken by currentSubdistrictId and currentSubdistrictName were used for all options.
Closures are really tricky in js when used with vars (sort of global scope).
Since you're using es6, you should properly use let (modified within a block like index in the for loop) and const (set only once when declared in a block) variables declaration and never use var (sort of global scope).
class App extends React.Component {
districtDataArray = [
{ name: 'A', id: 0 },
{ name: 'B', id: 1 },
{ name: 'C', id: 2 },
]
subdistrictDataArray = [
[
{ name: 'AA', id: 0 },
{ name: 'AB', id: 1 },
{ name: 'AC', id: 2 },
],
[
{ name: 'BA', id: 0 },
{ name: 'BB', id: 1 },
{ name: 'BC', id: 2 },
],
[
{ name: 'CA', id: 0 },
{ name: 'CB', id: 1 },
{ name: 'CC', id: 2 },
],
]
state = {
districtsSelectOptionsArrayState: this.districtDataArray.map(d => (
<option value={d.id}>
{d.name}
</option>
)),
subdistrictsSelectOptionsArrayState: [],
}
constructor(props) {
super(props);
this.updateDistrictData = this.updateDistrictData.bind(this);
}
updateDistrictData(e) {
this.setState({subdistrictsSelectOptionsArrayState : []});
const categoryList = document.getElementById("districts-select-list");
const selectedDistrictId = categoryList.options[categoryList.selectedIndex].value;
if(!selectedDistrictId) {
return;
}
const currentSubdistrictList = this.subdistrictDataArray[selectedDistrictId];
if(currentSubdistrictList) {
const currentSubdistrictListLength = currentSubdistrictList.length;
if(!currentSubdistrictListLength) {
return;
}
for(let index = 0; index < currentSubdistrictListLength; index++) {
// use const for block level constant variables
const currentDistrictObject = currentSubdistrictList[index];
if(currentDistrictObject) {
// use const for block level constant variables
const currentSubdistrictId = currentDistrictObject["id"];
const currentSubdistrictName = currentDistrictObject["name"];
console.log("SUBDISTRICT NAME IS : " + currentSubdistrictName);
const currentSubdistrictOption = (
<option value={currentSubdistrictId}>
{currentSubdistrictName}
</option>
);
this.setState(prevState => ({
subdistrictsSelectOptionsArrayState:[
...prevState.subdistrictsSelectOptionsArrayState,
(
<option value={currentSubdistrictId}>
{currentSubdistrictName}
</option>
)
]
}));
}
}
}
}
render() {
return (
<div>
<div style={{display: "inline-block", marginRight: "20px"}}>
<h style={{fontSize: "20px"}}>District</h>
<br/>
<select id="districts-select-list" onChange={this.updateDistrictData}>
{this.state.districtsSelectOptionsArrayState}
</select>
</div>
<div style={{display: "inline-block"}}>
<h style={{fontSize: "20px"}}>Subdistrict</h>
<br/>
<select id="subdistricts-select-list">
{this.state.subdistrictsSelectOptionsArrayState}
</select>
{this.state.subdistrictsSelectOptionsArrayState}
</div>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root" />
Also, the way you are updating the state in updateDistrictData is very inefficient (n + 1 setStates, n in a loop, n being the number of subdistricts).
You should compute the final state in a variable and set it all at once when computation is done.
While my implementation explains what was wrong with your code without altering it too much, Jared's answer below is a very good example of how it could be done cleaner.
This should solve the original problem, but this should hopefully solve a lot of other problems...
Your display code should look something like this:
<div style={{ display: "inline-block", marginRight: "20px" }}>
<h style={{ fontSize: "20px" }}>District</h>
<br />
<select id="districts-select-list" onChange={this.updateDistrictData}>
{this.state.districts.map(({ name, id }) => (
<option value={id} key={id}>{name}</option>
))}
</select>
</div>
<div style={{ display: "inline-block" }}>
<h style={{ fontSize: "20px" }}>Subdistrict</h>
<br />
<select id="subdistricts-select-list">
{
this.state
.subdistricts
.filter(({ districtId }) => districtId === this.state.selectedDistrictId)
.map(({ id, name }) => <option value={id} key={id}>{name}</option>)
}
</select>
</div>
And your update code now looks like this:
updateDistrictData (e) {
this.setState({ selectedDistrictId: e.target.value });
}
There's no point in storing all of those JSX react nodes in state, as long as you use the unique id for the key property React won't do unnecessary re-renders.
I would further recommend that you move the lists out of state entirely, and pass them in as props from a stateful parent component. Generally it's better to have components that display information to the user to be totally stateless and have the manipulation of state higher up the chain.