Cannot read property x of 'undefined' with ternary condition? - javascript

I made a table with a dropdown menu that will filter the data shown. It loads correctly and I can toggle the individual teams but when I try to select All Teams (index 0) again I get an error saying property 'name' is undefined.
What is wrong and how do I fix it?
import React, { useState } from "react";
import "./styles.css";
import { flavours } from "./mock-data";
export default function App() {
const [selectedFilter, setFilter] = useState(0);
// start table
const header = [
{ title: "Banana" },
{ title: "Chocolate" },
{ title: "Vanilla" },
{ title: "Total" }
];
// render Table Headers
const renderTableHeader = () =>
header.map((e, index) => {
const { title } = e;
return (
<th key={Number(index)}>
{title}
</th>
);
});
const renderAllTeamData = () =>
flavours.map((team) => {
const { name, banana, chocolate, vanilla } = team; // destructuring
return (
<tr key={team.name}>
<th
style={{ textAlign: "start" }}
>
{name}
</th>
<td>{banana.length}</td>
<td>{chocolate}</td>
<td>{vanilla}</td>
<td>
{banana.length + chocolate + vanilla}
</td>
</tr>
);
});
const renderTeamData = () => {
const { name, banana, chocolate, vanilla } = flavours[selectedFilter - 1]; // destructuring
return (
<tr>
<th style={{ textAlign: "start" }}>
{name}
</th>
<td>{banana.length}</td>
<td>{chocolate}</td>
<td>{vanilla}</td>
<td>{banana.length + chocolate + vanilla}</td>
</tr>
);
};
return (
<div className="App">
<form>
<select
value={selectedFilter}
onChange={(e) => setFilter(e.currentTarget.value)}
>
<option value={0}>All Teams</option>
{flavours.map((value, index) => (
<option key={value.name} value={index + 1}>
{value.name}
</option>
))}
</select>
</form>
<table>
<thead>
<tr>
<th> </th>
{renderTableHeader()}
</tr>
</thead>
<tbody>
{selectedFilter === 0 ? renderAllTeamData() : renderTeamData()}
</tbody>
</table>
</div>
);
}
Here is a code sandbox too https://codesandbox.io/s/nice-brattain-pwnbr?file=/src/App.js

The problem is here
{selectedFilter === 0 ? renderAllTeamData() : renderTeamData()}
Here you are using === which is comparing against value and type but you set the currentTarget.value which is a string, so the comparison fails and moved to the else part
<select
value={selectedFilter}
onChange={(e) => setFilter(e.currentTarget.value)}
>
You can fix by changing it to compare by value like below
{selectedFilter == 0 ? renderAllTeamData() : renderTeamData()}

You need to parse e.currentTarget.value to Int.
replace that line with :
onChange={(e) => setFilter(parseInt(e.currentTarget.value, 10))}
and it should work fine.

change the onchange function of your select
<select
value={selectedFilter}
onChange={(e) => {
setFilter(+e.currentTarget.value);
}}
>
<option value={0}>All Teams</option>
{flavours.map((value, index) => (
<option key={value.name} value={index + 1}>
{value.name}
</option>
))}
</select>
or change tbody like this
<tbody>
{selectedFilter == 0 ? renderAllTeamData() : renderTeamData()}
</tbody>
the problem in current scenario is the value u set is integer or number but value u select come as string

Convert the filter value to a number before setting it to state.
setFilter(+e.currentTarget.value)
const [selectedFilter, setFilter] = useState(0);
<form>
<select
value={selectedFilter}
onChange={(e) => setFilter(+e.currentTarget.value)}
>
<option value={0}>All Teams</option>
{flavours.map((value, index) => (
<option key={value.name} value={index + 1}>
{value.name}
</option>
))}
</select>
</form>

Related

js react converting dropdown to checkbox filter

Hej!
I am a newbie to javascript but was able to implement a dropdown menu filtering my json-data (everything works fine) but when I convert/change it into checkboxes I won't get any results in my result list.
// dropdown
const ContentBuilder = () => {
const options = [
{ label: "Landwirtschaft", value: "Landwirtschaft" },
{ label: "Forstwirtschaft", value: "Forstwirtschaft" },
]
const [newData, setNewData] = React.useState([]);
const [selectedValue, setSelectedValue] = React.useState(options[0]);
const filteredData = data.filter(x =>
x.Sektor == selectedValue)
const handleFilterInput = (event) => {
let value = event.target.value;
setSelectedValue(value);
};
return (
<section className="content">
<div className="container">
<div className="columns table">
<div className="column is-four-fifth filter">
<label>Sektion <br/>
<select
value={selectedValue}
onChange={handleFilterInput}
>
{options.map((option) => (
<option value={option.value}>{option.label}</option>
))}
</select>
</label>
</div>
<div className="column is-one-fifth">
<div className="container">
<div className="liste">
<List data={filteredData}/>
</div>
</div>
</div>
</div>
</div>
</section>
);
}
if I only change the select to input and add the 'checkbox' type all I get is an empty page
// checkboxes
.
.
.
<input
type = "checkbox"
className = "sektor-checkbox"
value={selectedValue}
onChange={handleFilterInput}
>
{options.map((option) => (
<option value={option.value}>{option.label}</option>
))}
</input>
.
.
.
If I put the 'checkbox' inside the map I get the checkboxes but no result list and therefor no filter.
.
.
.
{options.map((option) => (
<>
<input
type = "checkbox"
className = "sektor-checkbox"
value={selectedValue}
onChange={handleFilterInput}
>
</input>
<option value={option.value}>{option.label}</option>
</>
))}
.
.
.
// json
[
{
"Pflanzenname": ["Hanf (Samen-/Faser-)"],
"Sektor": "Landwirtschaft",
},{
"Pflanzenname": "Soja",
"Sektor": "Landwirtschaft",
},{
"Pflanzenname": "Hirse (Sorghum-/Zucker-)",
"Sektor": "Landwirtschaft",
},{
"Pflanzenname": "Riesenweizengras",
"Sektor": "Forstwirtschaft",
}
]
working dropdown menu:
https://codesandbox.io/s/shy-bash-bj5f5s?file=/src/contentBuilder.js
not working checkboxes:
https://codesandbox.io/s/vigilant-sun-mh3rg9?file=/src/App.js
Does anybody know what I'm missing?
Any help is appreciated! :)
I hope this solution will be work for you , check this live example
Sandox
Use filter logic like this
const filteredData = data.filter(
(x) => x.Sektor === checked.find((item) => item === x.Sektor)
);
Read this doc GoodPractice
use checkbox list like this
<div className="checkList">
<div className="title">Your CheckList:</div>
<div className="list-container">
{options.map((item, index) => (
<div key={index}>
<input onChange={handleCheck} value={item.value} type="checkbox" />
<span>{item.value}</span>
</div>
))}
</div>
</div>
in the change handler write like this
const handleCheck = (event) => {
var updatedList = [...checked];
if (event.target.checked) {
updatedList = [...checked, event.target.value];
} else {
updatedList.splice(checked.indexOf(event.target.value), 1);
}
setChecked(updatedList);
};
Live example Sandbox

setState funtion is not validly updating the rows variable and hence it is not reflecting in the UI?

The purpose of this is code is to similar to a ToDo List functionality. The add button is supposed to add another array object to the array of rows which is iterated by rows.map and reflected in the UI by showing another TextField . The subtract button is supposed to remove the selected row by sending its id as a parameter to a function called DeleteMetric(id). However, when the subtract button is clicked, its removing the last added array element irrespective of the id sent( Its sending the correct ID because I have console logged it and tested it out. ) I want that row to be removed whose id has been sent as a parameter and not the last added one. Thank you in advance for helping me.
This is how the state has been defined.
let [rows, setRows] = useState([
{
id: 1090,
name: 'Enter Name',
unit: 'Enter Unit'
},
{
id: 3000,
name: 'RISHAV',
unit: 'Unit'
}
]);[enter image description here][1]
The SubmitMetric function
const submitMetric = () => {
const c = Math.floor(Math.random() * 1000);
const newMetric = {
id: c,
name: name,
unit: unit
}
setRows([...rows, newMetric]);
}
I had the DeleteMetric function as
const deleteMetric = async (id) => {
await setRows(rows.filter((element) => {
return element.id !== id;
}));
}
It was yielding the same result so I changed the function to explicitly mutating the rows variable,which also is not working.
The DeleteMetric function now.
const deleteMetric = async (id) => {
rows = await rows.filter((element) => {
return element.id !== id;
});
await setRows(rows);
}
The returned JSX.
return (
<div>
{rows.map((row) => (
<Table>
<TableRow>
<TableCell>
<FormControl className="form-full-width">
<TextField variant="outlined" defaultValue={row.id}/>
</FormControl>
</TableCell>
<TableCell>
<FormControl className="form-full-width">
<TextField variant="outlined" defaultValue={row.unit}/>
</FormControl>
</TableCell>
<TableCell align='center'>
<IconButton onClick={submitMetric}>
<AddIcon/>
</IconButton>
</TableCell>
<TableCell align='center'>
<IconButton onClick={async () => {
await deleteMetric(row.id);
}
}>
<RemoveIcon/>
</IconButton>
</TableCell>
</TableRow>
</Table>
))}
<div align='center' className='margin-1'>
<Button variant="contained" color="primary">
Submit
</Button>
</div>
</div>
);
}
You should not mutate the array. You modify the rows state when you re-assign the filtered array inside deleteMetric(). Try the following, it uses the state hook correctly without mutating it:
const deleteMetric = (id) => {
setRows(rows.filter((el) => el.id !== id));
}
You will also need to remove every async/await in your code. Non of them is necessary.
function App() {
let [rows, setRows] = React.useState([
{
id: 1090,
name: "Name 1",
unit: "Unit 1"
},
{
id: 3000,
name: "Name 2",
unit: "Unit 2"
},
{
id: 8439,
name: "Name 3",
unit: "Unit 3"
}
]);
const submitMetric = () => {
const c = Math.floor(Math.random() * 1000);
const newMetric = {
id: c,
// only added these as I don't know what name
// and unit was in your original code
name: `Name ${c}`,
unit: `Unit ${c}`
};
setRows([...rows, newMetric]);
};
const deleteMetric = (id) => {
setRows(rows.filter((el) => el.id !== id));
};
return (
<div>
{rows.map((row) => (
<table key={row.id}>
<tbody>
<tr>
<td>
<label className="form-full-width">
<input defaultValue={row.id} />
</label>
</td>
<td>
<label className="form-full-width">
<input defaultValue={row.unit} />
</label>
</td>
<td align="center">
<button onClick={submitMetric}>Add</button>
</td>
<td align="center">
<button onClick={() => deleteMetric(row.id)}>Remove</button>
</td>
</tr>
</tbody>
</table>
))}
</div>
);
}
ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>

how to add new row dynamically in reactable

I am trying to add new row onclick of accordion i.e while expand using reactable, attached the expected result.
I have showed the table structured data using Tr and Td from reactable but however, not sure to add the new row.
onclick of the arrow the dynamic row should expand,I tried to do so but wasn't able to achieve that.
class PolicyDetails extends Component {
showPolicyOperation = (e,models) => {
e.preventDefault();
const {callbacks} = this.props
const activeClass = document.querySelectorAll('.fa-angle-up')
const currentTarget = e.currentTarget;
if(currentTarget.classList.contains('fa-angle-up')){
currentTarget.classList.remove('fa-angle-up');
currentTarget.classList.add('fa-angle-down');
}else{
currentTarget.classList.remove('fa-angle-down');
currentTarget.classList.add('fa-angle-up');
}
activeClass && activeClass.forEach(node => {
node.classList.remove('fa-angle-up');
node.classList.add('fa-angle-down');
})
callbacks.fetchPoliciesWithId(models.id)
}
getHeaders = () => {
let headers = ([
<Th key="0" column=""></Th>,
<Th key="1" column="id">Policy Id</Th>,
<Th key="2" column="serviceType">Service</Th>,
<Th key="3" column="name">Policy Name</Th>,
<Th key="4" column="description">Policy Description</Th>,
<Th key="5" column="policyLabel">Policy Label</Th>,
<Th key="6" column="policyType">Policy Type</Th>,
<Th key="7" column="operation">Operation</Th>,
<Th key="8" column="action">Actions</Th>
])
return headers;
}
pageChange = (page) => {
this.cPoliciesData.params.page = page - 1 || undefined;
this.props.callbacks.fetchPolicies();
}
getRows = (models, idx) => {
const state = this.props.options._vState
let rows = ([
<Td key="0" column="">
<i className="fa pointer fa-angle-down"
aria-hidden="true" key = {idx} onClick={e => {
state.isPolicySelected = !state.isPolicySelected;
this.showPolicyOperation(e,models)
}}></i></Td>,
<Td key="1" column="id">{<a>{models.id}</a>}</Td>,
<Td key="2" column="serviceType">{models.serviceType || "--"}</Td>,
<Td key="3" column="name">{models.name || "--"}</Td>,
<Td key="4" column="description">{models.description || "--"}</Td>,
<Td key="5" column="policyLabel">{"--"}</Td>,
<Td key="6" column="policyType">{models.serviceType == 'tag' && models.policyType == 0 ? "Tag Based" : POLICY_TYPE[models.policyType].label}</Td>,
<Td key="7" column="operation">{"--"}</Td>,
<Td key="8" column="action">{"--"}</Td>,
]);
let operation = state.isPolicySelected && <Tr className="special-row">
<Th column="name">
<strong className="name-header">First Name, Last Name</strong>
</Th>
<Th column="age">
<em className="age-header">Age, years</em>
</Th>
</Tr>
rows.push(operation)
return rows;
}
render() {
const {options , callbacks} = this.props;
const {cPoliciesData, _vState} = options
return (
<Row className="m-t-md">
{/* <Col md={12}> */}
<PanelBody>
<Table data={cPoliciesData}
tableAttr={{ className: "table table-hover" }}
getHeaders={this.getHeaders}
getRowData={this.getRows}
pagination={true}
pageChange={this.pageChange}
>
</Table>
</PanelBody>
{/* </Col> */}
</Row>
)
}
}
You just need to add in array . and then with
UseEffect(()=>{
},[options._vState])

How can I pass multiple value by select in reactjs

I need to pass multiple value by using onChange when I select the option, but I can not select single option . it select whole objects .
Here is my code .
const test = [{id:1, name:'test, value:{x:10}}]
<Select
showSearch
style={{ width: 200 }}
placeholder="Select a person"
optionFilterProp="children" >
{test.map(item =>(
<Option value={item.id, item.value}>{item.name}</Option>
))}
</Select>
is there an alternative solution to fix this problem
Use Custom select instead of select.
add multiple values in "eventKey" and on select handler you will find that value array.
import { ButtonToolbar,DropdownButton,MenuItem } from "react-bootstrap";
<ButtonToolbar className="snap-fitness-wrapper">
<DropdownButton
title={this.state.selectedFacilityName}
onSelect={this.onSelectFacility}
id="snap" >
{
this.state.facilities.map(facility => (
<MenuItem
key={facility.facilityId}
eventKey={[facility.facilityId, facility.name]}
value={facility.facilityId}
> {facility.name} - {facility.facilityId}{" "}
</MenuItem>
))
}
</DropdownButton>
</ButtonToolbar>
You can try below code.
const test = [{id:1, name:'test, value:{x:10}}]
<Select
multiple
showSearch
style={{ width: 200 }}
placeholder="Select a person"
optionFilterProp="children" >
{test.map(item =>(
<Option value={item.id, item.value}>{item.name}</Option>
))}
</Select>
If you are using react-select you can add isMulti common props to enable multiple selection for your option , please refer: https://react-select.com/props
BTW, react-select default use value label match, you have to remap for your case
const options = [
{ id: 1, name: 'test1', value:{x:10} },
{ id: 2, name: 'test2', value:{x:20} },
]
<Select
... // other code
isMulti
getOptionValue={option => option.value.x}
/>
It support multiple select , sample link : sandbox
I suppose you are using antd for select and want to get the object properties inside handleChange of select.
A simple method would be to send the id or other parameter and get the object through that.
import React from "react";
import ReactDOM from "react-dom";
import { Select } from "antd";
import "./styles.css";
const { Option } = Select;
const test = [
{ id: 1, name: "test1", value: { x: 10 } },
{ id: 2, name: "test2", value: { x: 11 } },
{ id: 3, name: "test3", value: { x: 12 } }
];
class App extends React.Component {
handleChange = id => {
const clickedOption = test.find(item => item.id === id);
const value = `${clickedOption.id}, ${clickedOption.value.x}`;
console.log(value);
};
render() {
return (
<div className="App">
<Select
onChange={this.handleChange}
style={{ width: 200 }}
placeholder="Select a person"
optionFilterProp="children"
>
{test.map((item, index) => (
<Option value={item.id} key={index}>
{item.name}
</Option>
))}
</Select>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Hope that helps!!!
The simple way of doing this can be like this:
let options = test.map(item => {
return <option value={item.value} onClick={(item) => this.yourhandlerfunction(item)}>item.name</option>
})
render(){
return(
<Select>
{options}
</Select>
)
}
Hope this helps!
You can just pass a second variable in onChange=((e) => this.handleChange(e, item.value) property
const handleChange = (event, itemValue) => {
console.log(`id: ${event.target.value}, value: ${itemValue}`)
}
const test = [{id:1, name:'test', value:{x:10}}]
<Select
multiple
showSearch
style={{ width: 200 }}
placeholder="Select a person"
optionFilterProp="children"
onChange={(e) => this.handleChange(e, item.value)}
>
{test.map((item) =>(
<Option key={key} value={item.id}>{item.name}</Option>
))}
</Select>

In React JS, using react-select want to set options to default value after checkbox unchecked

I am trying to, disable select list after unchecking checkbox and resetting the select value to default..but I am seeing the one I selected. I am using React-select for the select and options in this.
APP.js
const adultOptions = [{ value: '1', label: '1' },{ value: '2', label: '2' }];
class App extends Component {
state = {
disableSelect2: true
}
selectDisableHandler2 = () => {
const showSelect = this.state.disableSelect2;
this.setState({ disableSelect2: !showSelect });
}
render() {
return (
<>
<div className="Container">
<div>
<table style={{ borderStyle: "solid" }}>
<caption style={{ borderStyle: "inherit" }}>
<input type="checkbox"
onClick={this.selectDisableHandler2} />Room 2</caption>
<tbody>
<tr>
<th>Adult (18+)</th>
<th>Children (0-17)</th>
</tr>
<tr>
<td>
<Select
options={adultOptions}
isDisabled={this.state.disableSelect2}
defaultValue={this.state.disableSelect2 ? adultOptions[0] : null}/>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</>
);
}
}
export default App;
```[In the image after unchecking checkbox, the options value is set to "2" which I selected, but I wanted it to be default value i.e. "1" ][1]
[1]: https://i.stack.imgur.com/S5gjP.png
You need to handle the value of your select in your components state:
<Select
value={this.state.selectedOption}
isDisabled={this.state.disableSelect2}
options={adultOptions}
onChange={this.handleChange}
/>
Have the checkbox handler update it accordingly:
selectDisableHandler2 = () => {
const showSelect = this.state.disableSelect2;
this.setState({
disableSelect2: !showSelect,
selectedOption:
showSelect === true ? this.state.selectedOption : adultOptions[0]
});
};
Codesandbox:
https://codesandbox.io/s/6l2171x37n

Categories