Table - After deleting some rows, the next rows become/stay selected - reactjs

When I drop some rows in my table, the next rows become/stay selected.
In my state I have selectedStudents and tableData. The selectedStudents are all selected index in my table.
When I click on delete button.. the follow code remove all rows in my tableData and all values in selectedStudents.
let diff = _.remove(this.state.tableData, (it, idx) => !~this.state.selectedStudents.indexOf(idx))
this.setState({ tableData: diff, selectedStudents: [] })
The problem is that the rows are deleted but the next rows are selected, and I don't know why
See full image with states details here
This is the full code for this component... or check in WebPack bin
export default React.createClass({
getInitialState: function() {
return {
selectedStudents: [],
tableData: [
{ name: 'John Smith' },
{ name: 'Randal White' },
{ name: 'Stephanie Sanders' },
{ name: 'Steve Brown' },
{ name: 'Joyce Whitten' },
{ name: 'Samuel Roberts' },
{ name: 'Adam Moore' }
]
}
},
// Methods
_onStudentSelected (selected) {
let selectedObjs = []
if (_.isArray(selected)) { // Multiples selections
selectedObjs.push(...selected)
} else if (_.isNumber(selected)) { // One Selection
selectedObjs.push(selected)
} else if (_.isString(selected) && selected === 'all') { // Select all elements
selectedObjs.push(...this.state.tableData.map((it, idx) => idx))
} // None selected
this.setState({ selectedStudents: selectedObjs })
},
_onClickDeleteStudent () {
let diff = _.remove(this.state.tableData, (it, idx) =>
!~this.state.selectedStudents.indexOf(idx))
this.setState({ tableData: diff, selectedStudents: [] })
},
render: function() {
return (
<Table fixedHeader multiSelectable onRowSelection={this._onStudentSelected}>
<TableBody deselectOnClickaway={false}>
{this.state.tableData.map((row, index) => (
<TableRow key={row.name} selected={this.state.selectedStudents.indexOf(index) !== -1}>
<TableRowColumn>
<TextField value={row.name} hintText='Name' />
</TableRowColumn>
<TableRowColumn>
<TextField hintText='Password' type='password' />
</TableRowColumn>
</TableRow>
))}
</TableBody>
<TableFooter adjustForCheckbox>
<TableRow>
<TableRowColumn>
<RaisedButton label='Delete' onClick={this._onClickDeleteStudent}
disabled={this.state.selectedStudents.length <= 0} />
</TableRowColumn>
</TableRow>
</TableFooter>
</Table>
);
}
});
Versions
Package | Version
----------- | ------------------------------------
material-ui | ^0.17.1
react | ^15.4.2
Browser | Chrome Version 56.0.2924.87 (64-bit)
ADDED
Was reported as a bug on issue 6496

In the current stable version 16.7, onRowSelection(indices) of Table component, indices does not reflect the right state after deleting last rows. You may checkout the fixed version via: npm install --save material-ui#next, but it is still under testing so far. Cheers.
Edited:
M.Quan changed the source code and it works okay for me by the way.
Please see https://github.com/callemall/material-ui/issues/6006
"I found the cause of the error at line 131 in
https://github.com/callemall/material-ui/blob/master/src/Table/TableBody.js
componentWillReceiveProps(nextProps) {
if (this.props.allRowsSelected !== nextProps.allRowsSelected) {
if (!nextProps.allRowsSelected) {
this.setState({
selectedRows: [],
});
} else {
this.setState({
selectedRows: this.calculatePreselectedRows(nextProps),
});
}
}
}
When i change that code with the code in version 0.16.6. It worked with me!
componentWillReceiveProps(nextProps) {
if (this.props.allRowsSelected && !nextProps.allRowsSelected) {
this.setState({
selectedRows: this.state.selectedRows.length > 0 ?
[this.state.selectedRows[this.state.selectedRows.length - 1]] : [],
});
// TODO: should else be conditional, not run any time props other than allRowsSelected change?
} else {
this.setState({
selectedRows: this.calculatePreselectedRows(nextProps),
});
}
}
"

Here a simplified modern refactored version of your example :
import React, {Component} from 'react'
import Table from 'material-ui/Table/Table'
import TableBody from 'material-ui/Table/TableBody'
import TableHeader from 'material-ui/Table/TableHeader'
import TableFooter from 'material-ui/Table/TableFooter'
import TableHeaderColumn from 'material-ui/Table/TableHeaderColumn'
import TableRow from 'material-ui/Table/TableRow'
import TableRowColumn from 'material-ui/Table/TableRowColumn'
import TextField from 'material-ui/TextField/TextField'
import RaisedButton from 'material-ui/RaisedButton/RaisedButton'
export default class MyTable extends Component {
state = {
selectedStudents: [],
tableData: [
{ id: 0, name: 'John Smith' },
{ id: 1, name: 'Randal White' },
{ id: 2, name: 'Stephanie Sanders' },
{ id: 3, name: 'Steve Brown' },
{ id: 4, name: 'Joyce Whitten' },
{ id: 5, name: 'Samuel Roberts' },
{ id: 6, name: 'Adam Moore' }
]
}
// Methods
_onStudentSelected = (selected) => {
let selectedStudents = selected === 'all'// Select all elements
? [...this.state.tableData].map(({id}) => id)
: selected
console.log('selected', selected,
'\nSelected students', selectedStudents)
this.setState({selectedStudents})
}
_onClickDeleteStudent = () => {
let tableData = this.state.tableData.filter(({id}) =>
!this.state.selectedStudents.includes(id)
)
this.setState({tableData, selectedStudents: []}, () => {
console.log('After delete', this.state.tableData,
'\nthis.state.selectedStudents', this.state.selectedStudents)
})
}
render () {
console.debug('rendered selectedStudents', this.state.selectedStudents)
const isSelected = id => {
let result = this.state.selectedStudents.includes(id)
console.log('id', id, 'result', result)
return result
}
return (
<Table fixedHeader multiSelectable enableSelectAll onRowSelection={this._onStudentSelected}>
<TableHeader>
<TableRow>
<TableHeaderColumn>Name</TableHeaderColumn>
<TableHeaderColumn>Password</TableHeaderColumn>
</TableRow>
</TableHeader>
<TableBody deselectOnClickaway={false}>
{this.state.tableData.map(({id, name}) => (
<TableRow key={id} selected={isSelected(id)}>
<TableRowColumn>
<TextField value={name} hintText='Name' />
</TableRowColumn>
<TableRowColumn>
<TextField hintText='Password' type='password' />
</TableRowColumn>
</TableRow>
))}
</TableBody>
<TableFooter adjustForCheckbox>
<TableRow>
<TableRowColumn>
<RaisedButton label='Delete' onTouchTap={this._onClickDeleteStudent}
disabled={!this.state.selectedStudents.length} />
</TableRowColumn>
</TableRow>
</TableFooter>
</Table>
)
}
}
Hope it helps despite it not solving your issue. You should raise an issue in Material-UI repo.

please use onTouchTap instead of onClick

Related

react table select rows - programmatically select rows based on input props

I have created a react table with select rows following this example.
I'm trying to modify it so that when the data loads, the corresponding checkbox is either checked or unchecked based on the row data's included value. The value doesn't seem to be recognized and when I check/uncheck a row the onChange console.log event isn't being fired. What am I doing wrong.
Heres my Sandbox Example
DATA
[
{
systemId: 13,
deqId: "25007",
facilityId: 6487,
sourceId: "WS002",
sourceName: "GROVE SPRING",
flowRate: 461,
flowUom: "GPM ",
included: true
},
{
systemId: 13,
deqId: "25007",
facilityId: 4742,
sourceId: "WS004",
sourceName: "WELL #1",
flowRate: 1100,
flowUom: "GPM ",
included: true
},
{
systemId: 13,
deqId: "25007",
facilityId: 4743,
sourceId: "WS005",
sourceName: "100 W (WELL #2) ",
flowRate: 800,
flowUom: "GPM ",
included: true
},
{
systemId: 13,
deqId: "25007",
facilityId: 4744,
sourceId: "WS007",
sourceName: "NORTH (WELL #3) ",
flowRate: 900,
flowUom: "GPM ",
included: true
}
];
INDETERMINATE CHECKBOX
const IndeterminateCheckbox = React.forwardRef(
({ indeterminate, checked, name, ...rest }, ref) => {
const defaultRef = React.useRef(checked);
const resolvedRef = ref || defaultRef;
React.useEffect(() => {
resolvedRef.current.indeterminate = indeterminate;
resolvedRef.current.checked = checked;
}, [resolvedRef, indeterminate, checked]);
return (
<>
<input
type="checkbox"
ref={resolvedRef}
checked={checked}
name={name}
id={name}
{...rest}
/>
</>
);
}
);
REACT TABLE
function ReactTable({
columns,
data,
handleCheckboxSelection,
handleCheckboxStateChange
}) {
// Use the state and functions returned from useTable to build your UI
const {
getTableProps,
getTableBodyProps,
headerGroups,
footerGroups,
rows,
prepareRow,
selectedFlatRows
} = useTable(
{
columns,
data
},
useRowSelect,
(hooks) => {
hooks.visibleColumns.push((columns) => [
// Let's make a column for selection
{
id: "selection",
// The header can use the table's getToggleAllRowsSelectedProps method
// to render a checkbox
Header: (
{ getToggleAllRowsSelectedProps },
handleCheckboxStateChange
) => (
<div>
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
</div>
),
// The cell can use the individual row's getToggleRowSelectedProps method
// to the render a checkbox
Cell: ({ row }) => (
<div>
<IndeterminateCheckbox
name={row.original.sourceId}
onChange={(row) => console.log(row.original)} //not firing
checked={row.original.included}
{...row.getToggleRowSelectedProps()}
/>
</div>
)
},
...columns
]);
}
);
// Render the UI for your table
return (
<>
<table {...getTableProps()}>
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th {...column.getHeaderProps()}>{column.render("Header")}</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.slice(0, 10).map((row, i) => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map((cell) => {
return (
<td {...cell.getCellProps()}>{cell.render("Cell")}</td>
);
})}
</tr>
);
})}
</tbody>
<tfoot>
{footerGroups.map((group) => (
<tr {...group.getFooterGroupProps()}>
{group.headers.map((column) => (
<td {...column.getFooterProps()}>
<b>{column.render("Footer")}</b>
</td>
))}
</tr>
))}
</tfoot>
</table>
<button onClick={() => handleCheckboxSelection(selectedFlatRows)}>
Save
</button>
</>
);
}
TABLE IMPLEMENTATION
const MyDataTable = ({
data
}) => {
const handleCheckboxSelection = (array) => {
console.log(array.map((d) => d.original));
};
const columns = React.useMemo(
() => [
{
Header: "Source ID",
accessor: "sourceId"
},
{
Header: "Source Name",
accessor: "sourceName"
},
{
Header: "Flow Rate (GPM)",
accessor: (d) => {
return d.flowRate ? numberWithCommas(d.flowRate) : "";
}
}
],
[]
);
return (
<ReactTable
columns={columns}
data={data}
handleCheckboxSelection={handleCheckboxSelection}
/>
);
};
The props you added to IndeterminateCheckbox are being overwritten. row.getToggleRowSelectedProps() returns an object:
{
onChange: function,
checked: Boolean,
title: String,
indeterminate: Boolean,
style: Object
}
which overwrites your properties.
The correct way to do what you want to do would be to use
initialState.selectedRowIds property from the useRowSelect API.
Map your data to their included values, then add that array to the initialState as selectedRowIds. In ReactTable.js:
const selectedRowIds = data.map((d, ind) => d.included)
const {
// etc
} = useTable(
{
columns,
data,
initialState: {selectedRowIds}
},
//etc
...columns
}
);
you have to refresh your Table.
you can use useState.
example : you can add onClick={this.mychange} to your save Button.
mychange = async () => {
this.setState({
List: //the new List Data
})
}
and dont forget the Constructor.
constructor() {
super();
this.state = {
List: anyList,
};
}

How to update the state of array when onclick in the image(icon) without refresh page in react js

i am fetching some array of items with the expample url('https://www.getadata.com/items') and using authentication fetching some more wishes with the example url('https://www.getadata.com/wishes'), i had mixing both array data with matching UID, displaying in the ui....if the true value showing the radio checked icon or false to radio unchecked icon , now i want to post the true or false values of wish with onclick on the radio button..i am maintaining the like , after onclick the icon...
i can post successfully...but i cannot see in the UI....after refreshing page then i can see updated array in the UI....how to update the array without refreshing page and see updated array in the UI
Please help me on this....
code sand box link : https://codesandbox.io/s/how-to-push-the-array-of-objects-data-into-another-array-with-matching-uid-forked-dcov3?file=/demo.js:0-3585
import React, { Component } from "react";
import Table from "#material-ui/core/Table";
import TableBody from "#material-ui/core/TableBody";
import TableCell from "#material-ui/core/TableCell";
import TableContainer from "#material-ui/core/TableContainer";
import TableHead from "#material-ui/core/TableHead";
import TableRow from "#material-ui/core/TableRow";
import RadioButtonCheckedIcon from "#material-ui/icons/RadioButtonChecked";
import RadioButtonUncheckedIcon from "#material-ui/icons/RadioButtonUnchecked";
import axios from "axios";
class Wish extends Component {
constructor() {
super();
this.state = {
items: [
{ uid: "1001", item: "iPhone" },
{ uid: "1002", item: "iPad" },
{ uid: "1003", item: "iTab" },
{ uid: "1004", item: "iTelevision" }
],
wishes: [
{ uid: "1001", wish: true },
{ uid: "1002", wish: null },
{ uid: "1003", wish: true },
{ uid: "1004", wish: null }
],
UserToken: sessionStorage.getItem('UserToken'),
};
}
componentDidMount() {
axios
.get("https://www.getadata.com/items")
.then((resposne) => {
this.setState({
items: resposne.data.items
});
})
.catch((error) => {
console.log(error);
});
if (this.state.UserToken) {
axios
.get("https://www.getadata.com/wishes")
.then((resposne) => {
this.setState({
wishes: resposne.data.wishes
});
})
.catch((error) => {
console.log(error);
});
}
}
clickedIcon = (item) => {
if (this.state.UserToken) {
fetch("https://www.getadata.com/wishes", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Token ${this.state.UserToken}`
},
body: JSON.stringify({
courses: [
{
uid: item.uid,
wish: !item.wish
}
]
})
}).catch((error) => {
console.log(error);
});
}
};
render() {
const itemsObj = this.state.items.reduce((itemsObj, item) => {
return {
...itemsObj,
[item.uid]: item
};
}, {});
const mergedObj = this.state.wishes.reduce((mergedObj, { uid, wish }) => {
return {
...mergedObj,
[uid]: {
...mergedObj[uid],
wish
}
};
}, itemsObj);
return (
<div>
<TableContainer>
<Table>
<TableHead>
<TableRow>
<TableCell>UID</TableCell>
<TableCell align="left">Item</TableCell>
<TableCell align="left">Wish</TableCell>
</TableRow>
</TableHead>
<TableBody>
{Object.values(mergedObj).map((item) => (
<TableRow key={item.name}>
<TableCell component="th" scope="row">
{item.uid}
</TableCell>
<TableCell align="left">{item.item}</TableCell>
<TableCell
onClick={() => this.clickedIcon(item)}
align="left"
>
{item.wish === true ? (
<RadioButtonCheckedIcon />
) : (
<RadioButtonUncheckedIcon />
)}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</div>
);
}
}
export default Wish;

Set value to state React js

I need a bit of help.
I am new to react, so I have stuck here. I have shared a sandbox box link. That Contains a Table. as below
| Toy | Color Available | Cost Available |
Now everything works perfectly. But I want to save the data of the table as below
The detail state should contain a list of row values of the table and the columnsValues should contain the checkbox value of Color Available and Cost Available
Example:
this.state.detail like
detail: [
{
toy : ...
color : ...
cost : ...
}
{
toy : ...
color : ...
cost : ...
}
...
...
...
]
this.state.columnsValues like
columnsValues: {
color : boolean
cost : boolean
}
Any experts please help me out. I am struggling from past few hours.
Thank you.
Sandbox link: https://codesandbox.io/s/suspicious-microservice-qd3ku?file=/index.js
just paste this code it is working .
check your console you'll get your desired output .
import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Table, Checkbox, Input } from "antd";
import { PlusCircleOutlined, MinusCircleOutlined } from "#ant-design/icons";
const { Column } = Table;
class ToyTable extends React.Component {
constructor(props) {
super(props);
this.state = {
dataSource: [
{
key: 0,
toy: "asdf",
color: "black",
cost: "23"
}
],
count: 0,
colorSwitch: false,
costSwitch: false,
columnsValues: {
color: true,
cost: true
},
detail: []
};
}
componentDidMount(){
const count = this.state.dataSource.length;
this.setState({
count
})
}
handleAdd = () => {
const { dataSource } = this.state;
let count = dataSource.length;
const newData = {
key: count,
toy: "",
color: "",
cost: ""
};
this.setState({
dataSource: [...dataSource, newData],
count
});
};
handleDelete = key => {
const dataSource = [...this.state.dataSource];
this.setState({ dataSource: dataSource.filter(item => item.key !== key) });
};
onChangecolor = (e, record) => {
let dataSource = this.state.dataSource;
let key = record.key;
dataSource[key].color = e.target.value;
this.setState({
dataSource
});
};
onChangeCost = (e, record) => {
let dataSource = this.state.dataSource;
let key = record.key;
dataSource[key].cost = e.target.value;
this.setState({
dataSource
});
};
onChangeToy = (e, record) => {
console.log("I am inside handleInputChange", e.target.value, record);
let dataSource = this.state.dataSource;
let key = record.key;
dataSource[key].toy = e.target.value;
this.setState({
dataSource
});
};
checkColor = e => {
this.setState({ colorSwitch: e.target.checked });
};
checkCost = e => {
this.setState({ costSwitch: e.target.checked });
};
render() {
const { dataSource } = this.state;
console.log(dataSource);
return (
<Table bordered pagination={false} dataSource={dataSource}>
<Column
title="Toy"
align="center"
key="toy"
dataIndex="toy"
render={(text, record) => (
<Input
component="input"
className="ant-input"
type="text"
value={record.toy}
onChange={e => this.onChangeToy(e, record)}
/>
)}
/>
<Column
title={() => (
<div className="row">
Color Available
<div className="col-md-5">
<Checkbox size="small" onChange={this.checkColor} />
</div>
</div>
)}
align="center"
dataIndex="color"
render={(text, record) => (
<Input
disabled={!this.state.colorSwitch}
value={record.color}
onChange={e => this.onChangecolor(e, record)}
component="input"
className="ant-input"
type="text"
/>
)}
/>
<Column
title={() => (
<div className="row">
Cost Available
<div className="col-md-5">
<Checkbox size="small" onChange={this.checkCost} />
</div>
</div>
)}
align="center"
dataIndex="color"
render={(text, record) => (
<Input
disabled={!this.state.costSwitch}
value={record.cost}
onChange={e => this.onChangeCost(e, record)}
component="input"
className="ant-input"
type="text"
/>
)}
/>
<Column
render={(text, record) =>
this.state.count !== 0 && record.key + 1 !== this.state.count ? (
<MinusCircleOutlined
onClick={() => this.handleDelete(record.key)}
/>
) : (
<PlusCircleOutlined onClick={this.handleAdd} />
)
}
/>
</Table>
);
}
}
ReactDOM.render(<ToyTable />, document.getElementById("container"));
This isn't an exact answer, but just as a general direction - you need something in the state to capture the values of the currently edited row contents, that you can then add to the final list. This is assuming once committed, you don't want to modify the final list.
Firstly, have an initial state that stores the values in the current row being edited
this.state = {
currentData: {
toy: '',
color: '',
..other props in the row
}
...other state variables like dataSource etc
}
Secondly, when the value in an input box is changed, you have to update the corresponding property in the currentData state variable. I see that you already have a handleInputChange function
For eg, for the input box corresponding to toy, you'd do
<input onChange={e => handleInputChange(e, 'toy')} ...other props />
and in the function itself, you'd update the currentData state variable, something like:
handleInputChange = (e, property) => {
const data = this.state.currentData
data[property] = e.target.value
this.setState({ currentData: data })
}
Finally, when you press add, in your handleAddFunction, you want to do two things:
1) use the currentData in state, that's been saving your current values and push them into the dataSource or details array
2) restore the currentData to the blank state, ready to track updates for the next row.
handleAdd = () => {
const { count, dataSource } = this.state;
const newData = {
key: count,
...this.state.newData,
};
this.setState({
dataSource: [...dataSource, newData],
count: count + 1,
currentData: {
toy: '',
// other default values
}
});
};

React listen for updated data in service then pass the data to Admin dataProvider in another component

Still new to React. As the subject states, I want to listen for data that comes through my service, which is the following:
import axios from "axios"
const base_url = 'https://localhost:5001/OrderItem';
export let orderItemByDivision = {
data: []
}
export const getOrderItemByDiv = (div, yr) => {
orderItemByDivision.data = axios.get(base_url + "/" + div + "/" + yr);
return axios.get(base_url + "/" + div + "/" + yr)
}
The aforementioned service is triggered by selecting a year from a drop down menu, followed by selecting a division, which sends a get to an API responding with JSON
import React from 'react';
import { FormGroup, FormControl, Button, IconButton, Menu, MenuItem } from '#material-ui/core';
import { getDivision } from '../services/division-service';
import { getOrderItemByDiv } from '../services/order-item-service';
import { MuiThemeProvider } from '#material-ui/core/styles';
import MoreVertIcon from '#material-ui/icons/MoreVert';
import theme from '../theme';
export default class DivisionDropDownComponent extends React.Component {
state = {
divData: [],
divValue: 'Select Division',
divOpen: false,
divAnchorEl: null,
yrValue: '2020',
yrOpen: false,
yrAnchorEl: null,
yrs: ['2020','2019','2018','2017','2016','2015','2014','2013','2012']
}
constructor(props) {
super(props);
this.handleDivChange = this.handleDivChange.bind(this);
}
componentDidMount() {
getDivision()
.then((res) => {
this.setState({ divData: res.data });
})
.catch(error => {
console.error(error);
});
}
handleDivChange = (divData) => {
// this.setState({value: event.target.value ? event.target.value : ''});
console.log("divData selected: " + divData.code_division);
getOrderItemByDiv(divData.code_division, this.state.yrValue)
.then((res) => {
console.log(res.data);
})
.catch(error => {
console.error(error);
});
this.onCloseDiv();
}
handleYrChange = (event) => {
// this.setState({value: event.target.value ? event.target.value : ''});
this.setState({ yrValue: event.target.value });
console.log("divData selected: " + event.target.value);
this.onCloseYr();
}
handleDivClick = (event) => {
this.setState({ divAnchorEl: event.target })
this.setState({ divOpen: true });
}
handleYrClick = (event) => {
this.setState({ yrAnchorEl: event.target })
this.setState({ yrOpen: true });
}
onCloseDiv = () => {
this.setState({ divOpen: false });
}
onCloseYr = () => {
this.setState({ yrOpen: false });
}
render(){
let arrayOfData = this.state.divData;
let dropdowns = arrayOfData.map((divData) =>
<MenuItem onClick={(event) => this.handleDivChange(divData)} key={divData.code_division}
value={divData.code_division} text={divData.code_division}>
{divData.code_division}
</MenuItem>
);
let arrayOfYrs = this.state.yrs;
let yrDropDown = arrayOfYrs.map((yrs) =>
<MenuItem onClick={(event) => this.handleYrChange(event)} value={yrs} key={yrs}>
{yrs}
</MenuItem>
);
return (
<MuiThemeProvider theme={theme}>
<FormGroup column='true'>
<FormControl>
<IconButton
aria-label="more"
aria-controls="long-menu"
aria-haspopup="true"
onClick={this.handleDivClick}
>
<MoreVertIcon />
</IconButton>
<Menu id="div-menu" anchorEl={this.state.divAnchorEl} open={this.state.divOpen} onClose={this.onCloseDiv}
className='dropDownDiv' defaultValue={this.state.divValue ? this.state.divValue: ''} >
<MenuItem value="Select Division">
Select Division
</MenuItem>
{dropdowns}
</Menu>
<Button aria-controls="simple-menu" aria-haspopup="true" onClick={this.handleYrClick}>
Select Year
</Button>
<Menu id="yrs-menu" open={this.state.yrOpen}
anchorEl={this.state.yrAnchorEl} onClose={this.onCloseYr}
defaultValue={this.state.yrValue ? this.state.yrValue: ''} >
{yrDropDown}
</Menu>
</FormControl>
</FormGroup>
</MuiThemeProvider>
)
}
}
As you can see by the screen shot, the data is coming through in the console. How do I have the Admin dataProvider listen to any changes coming from the order-item-service.jsx file?
Can I place an observable on the orderItemByDivision.data?
As usual, thanks in advance
Editing my post to give more detail to my question. In Angular, I used HttpClient and RxJS to subscribe and observe. I have tried the following in React, but it is having issues with setState:
import { ajax } from "rxjs/ajax";
import { Observable } from "rxjs";
const base_url = "https://localhost:5001/OrderItem";
export let orderItemByDivision = {
data: []
};
export const getOrderItemByDiv = (div, yr) => {
return new Observable(observe => {
orderItemByDivision.data = ajax
.get(base_url + "/" + div + "/" + yr)
.subscribe(resu => {
setState({ orderItemByDivision.data: resu });
observe.next(resu);
});
});
};
See the "orderItemByDivision.data" it is having issues with the dot notation. Since this is a service and not within a component class, I cannot call this.setState. How can I set the state, so that I can observe and subscribe to the ajax request?
Thanks in advance
I just meant smt like this.
class ParentComp extends Component {
constructor (props) {
super(props);
this.state = {
data: {}
};
}
editData = newData => {
this.setState({
data: newData
});
};
render () {
return <ChildComp editData={ this.editData } />;
}
}
class ChildComp extends Component {
render () {
return <Button onCLick={ () => this.props.editData(someData) } />;
}
}
You can update the parent comp state from child comp.
OK, I was able emulate what I have done in Angular 6, which is to create an observable, which can then be subscribed (listen for changes)
First, the order-item-service.jsx:
import { Subject } from 'rxjs';
import { ajax } from "rxjs/ajax";
import { Observable } from "rxjs";
const subject = new Subject();
const base_url = 'https://localhost:5001/OrderItem';
export let orderItemByDivision = {
data: []
}
export const getOrderItemByDiv = (div, yr) => {
return new Observable(observe => {
orderItemByDivision.data = ajax
.get(base_url + "/" + div + "/" + yr)
.subscribe(resu => {
orderItemByDivision.data = resu.response ;
observe.next(resu.response);
});
});
};
export const messageService = {
sendMessage: message => subject.next({ message }),
clearMessage: () => subject.next(),
getMessage: () => subject.asObservable()
}
As you can see, I tried to use the Subject from rxjs for sending data between components, but using ajax from rxjs/ajax was what ultimately lead me to be able to listen to changes
I proceeded to the same with the division-service.jsx service:
import { ajax } from "rxjs/ajax";
import { Observable } from "rxjs";
const base_url = 'https://localhost:5001/Division';
let state = {
data: []
}
export const getDivision = () => {
return new Observable(observe => {
state.data = ajax
.get(base_url)
.subscribe(resu => {
state.data = resu.response ;
observe.next(resu.response);
});
});
}
As you can see in both cases, it creates on observable after it subscribes to the get method - in other words, every time that the API URL is used, it will alert anyone who is subscribed to the two aforementioned methods.
The following division-dropdown-component.jsx component, receives the division JSON, which fills the drop down menu of divisions. Once the end user clicks on a division, the getOrderItemByDiv is subscribed, which returns the JSON that we are needing to populate the #material-ui Table - listed down below:
import React from "react";
import {
FormGroup,
FormControl,
Button,
Menu,
MenuItem
} from "#material-ui/core";
import { getDivision } from "../services/division-service";
import { getOrderItemByDiv } from "../services/order-item-service";
import { MuiThemeProvider } from "#material-ui/core/styles";
import theme from "../theme";
import { messageService } from "../services/order-item-service";
export default class DivisionDropDownComponent extends React.Component {
state = {
divData: [],
divValue: "Select Division",
divOpen: false,
divAnchorEl: null,
yrValue: "2020",
yrOpen: false,
yrAnchorEl: null,
yrs: [
"2020",
"2019",
"2018",
"2017",
"2016",
"2015",
"2014",
"2013",
"2012"
]
};
constructor(props) {
super(props);
this.handleDivChange = this.handleDivChange.bind(this);
}
componentDidMount() {
getDivision().subscribe(res => {
this.setState({ divData: res });
});
}
handleDivChange = divData => {
getOrderItemByDiv(divData.code_division, this.state.yrValue).subscribe(
res => {
this.setState({ divData: res });
messageService.sendMessage(res);
}
);
this.onCloseDiv();
};
handleYrChange = event => {
this.setState({ yrValue: event.target.value });
this.onCloseYr();
};
handleDivClick = event => {
this.setState({ divAnchorEl: event.target });
this.setState({ divOpen: true });
};
handleYrClick = event => {
this.setState({ yrAnchorEl: event.target });
this.setState({ yrOpen: true });
};
onCloseDiv = () => {
this.setState({ divOpen: false });
};
onCloseYr = () => {
this.setState({ yrOpen: false });
};
render() {
let arrayOfData = this.state.divData;
let dropdowns = arrayOfData.map((divData, index) => (
<MenuItem
onClick={event => this.handleDivChange(divData)}
key={index}
value={divData.code_division}
text={divData.code_division}
>
{divData.code_division}
</MenuItem>
));
let arrayOfYrs = this.state.yrs;
let yrDropDown = arrayOfYrs.map(yrs => (
<MenuItem
onClick={event => this.handleYrChange(event)}
value={yrs}
key={yrs}
>
{yrs}
</MenuItem>
));
return (
<MuiThemeProvider theme={theme}>
<FormGroup column="true">
<FormControl>
<Button
aria-controls="simple-menu"
aria-haspopup="true"
onClick={this.handleYrClick}
>
Select Year
</Button>
<Menu
id="yrs-menu"
open={this.state.yrOpen}
anchorEl={this.state.yrAnchorEl}
onClose={this.onCloseYr}
defaultValue={this.state.yrValue ? this.state.yrValue : ""}
>
{yrDropDown}
</Menu>
<Button
aria-controls="simple-menu"
aria-haspopup="true"
onClick={this.handleDivClick}
>
Select Division
</Button>
<Menu
id="div-menu"
anchorEl={this.state.divAnchorEl}
open={this.state.divOpen}
onClose={this.onCloseDiv}
className="dropDownDiv"
defaultValue={this.state.divValue ? this.state.divValue : ""}
>
<MenuItem value="Select Division">Select Division</MenuItem>
{dropdowns}
</Menu>
</FormControl>
</FormGroup>
</MuiThemeProvider>
);
}
}
The following is the order-item-component.jsx component, which fills the material-ui Table:
import React from "react";
import Table from "#material-ui/core/Table";
import TableBody from "#material-ui/core/TableBody";
import TableCell from "#material-ui/core/TableCell";
import TableContainer from "#material-ui/core/TableContainer";
import TableHead from "#material-ui/core/TableHead";
import TableRow from "#material-ui/core/TableRow";
import Paper from "#material-ui/core/Paper";
import { TablePagination } from "#material-ui/core";
import TableFooter from "#material-ui/core/TableFooter";
import { messageService } from "../services/order-item-service";
export default class OrderItemComponent extends React.Component {
state = {
data: [],
_columns: [],
Header: [],
totalCount: 10,
pageSize: 16,
page: 0
};
componentDidMount() {
componentDidMount() {
this.subscription = messageService.getMessage().subscribe(message => {
if (message) {
this.setState({ data: message.message });
this.setState({ totalCount: Math.ceil(this.state.data.length / this.state.pageSize) });
this.setState({ Header: ['order_id', 'order_item_id', 'product_id', 'code_division', 'code_product',
'quantity_due', 'quantity_shipped', 'price', 'date_shipped', 'date_due',
'customer_id','ship_via','value_due','value_shipped','date_order','date_modified'] });
} else {
// do nothing
this.setState({ data: [] });
}
})
}
componentWillUnmount() {
// unsubscribe to ensure no memory leaks
this.subscription.unsubscribe();
}
getOrderItem(){
this.setState({ data: messageService.getMessage() });
}
handleChangePage = (event, newPage) => {
this.setState({ page: newPage });
if (this.state.totalCount !== this.state.data.length) {
}
}
render() {
return (
<div>
<TableContainer component={Paper}>
<Table aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Order ID</TableCell>
<TableCell align="right">Qty Due</TableCell>
<TableCell align="right">Prod ID</TableCell>
<TableCell align="right">Div</TableCell>
<TableCell align="right">Prod Code</TableCell>
<TableCell align="right">Qty Sh</TableCell>
<TableCell align="right">Price</TableCell>
<TableCell align="right">Dt SH</TableCell>
<TableCell align="right">Dt Due</TableCell>
<TableCell align="right">Cust ID</TableCell>
<TableCell align="right">Ship Via</TableCell>
<TableCell align="right">Val Dt</TableCell>
<TableCell align="right">Val Sh</TableCell>
<TableCell align="right">Dt Or</TableCell>
<TableCell align="right">Dt Mod</TableCell>
</TableRow>
</TableHead>
<TableBody>
{this.state.data.map((row, index) => (
<TableRow key={ index }>
<TableCell component="td" scope="row">
{ row.order_id }
</TableCell>
<TableCell align="right">{ row.quantity_due }</TableCell>
<TableCell align="right">{ row.product_id}</TableCell>
<TableCell align="right">{ row.code_division }</TableCell>
<TableCell align="right">{ row.code_product }</TableCell>
<TableCell align="right">{ row.quantity_shipped }</TableCell>
<TableCell align="right">{ row.price }</TableCell>
<TableCell align="right">{ row.date_shipped}</TableCell>
<TableCell align="right">{ row.date_due }</TableCell>
<TableCell align="right">{ row.customer_id }</TableCell>
<TableCell align="right">{ row.ship_via }</TableCell>
<TableCell align="right">{ row.value_due }</TableCell>
<TableCell align="right">{ row.value_shipped }</TableCell>
<TableCell align="right">{ row.date_order }</TableCell>
<TableCell align="right">{ row.date_modified }</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter>
<TableRow>
<TablePagination
rowsPerPageOptions={ [5, 10, 25, { label: 'All', value: -1 }] }
colSpan={ 3 }
count={ this.state.data.length }
rowsPerPage={ this.state.pageSize }
page={ this.state.page }
SelectProps={ {
inputProps: { 'aria-label': 'rows per page' },
native: true,
} }
onChangePage={ this.handleChangePage }
/>
</TableRow>
</TableFooter>
</Table>
</TableContainer>
</div>
)
}
}
Now, you can see how I use the getMessage method to set the data
Almost all of it works, except the pagination - when I click on the "more" arrow, it will calculate correctly what the next set of data is, but it will not display the "new" data within the Table. I will have to figure out how to do a refresh on a #material-ui Table when pagination is involved, but nevertheless, I thought I would share in case someone else wanted to know how to create an observable.
I tried to use the example on #material-ui documentation but setPage = React.useState(5) kept causing an error having to do with invalid hook or something like that - regarding the following function:
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
I will post another question on that topic, but for now, the question that I asked has been answered
Thanks once more

How to collect Switch value in React JS + Ant Design

I'm sorry for my basic knowledge and my basic english :)
My Question is: How to concenate switch value [I use Switch for each record in table]
This is the table
https://ibb.co/3TVLBK6
I've created table with one cell/field using Switch (On/Off)
And i want get the switch value when they are on
And here is the code
const columnsTableDepartmentModal = [
{
title: 'No',
dataIndex: 'no',
key: 'no',
},
{
title: 'Department',
dataIndex: 'department',
key: 'department',
},
{
title: 'Select',
dataIndex: 'select_department',
key: 'select_department',
render: (e, record) => (
<Switch
defaultChecked={e}
onChange={
(value) => onChangeSwitch(value,record)
}
checkedChildren="Yes"
unCheckedChildren="No"
/>
),
}];
This is what i now try
function onChangeSwitch(isSelect,record){
console.log(e); // True / False
console.log(record); // True / False
if(isSelect){
// push data to array
}
if(!isSelect){
// pop data from array
}
}
This is how i show the table
<Modal
title={modalDepartmentTitle}
visible={visibleDepartment}
width={800}
onOk={handleOkDepartment}
onCancel={handleCancelDepartment}
footer={[
<Button key="submit" type="primary" onClick={handleOkDepartment}>OK</Button>,
<Button key="button" type="danger" onClick={handleDeleteDepartment}>DELETE</Button>,
<Button key="back" onClick={handleCancelDepartment}>CANCEL</Button>,
]}
>
<Table
columns={columnsTableDepartmentModal}
dataSource={stateDepartment.data}
pagination={false}
scroll={{y: 325}}
/>
</Modal>
Expected result: 1,3,4
import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './style.css';
import Switch from 'react-switch';
class App extends Component {
constructor() {
super();
this.state = {
checkedPos: [],
info: [],
};
}
componentWillMount()
{
let tmp = []
let pos = []
for (var i = 1; i < 6; i++) {
tmp.push("Info " + i)
pos.push(false);
}
this.setState({
info: tmp,
checkedPos: pos
})
}
handleChange(index, info)
{
if (!this.state.checkedPos[index])
{
this.setState(prev => ({
checkedPos: prev.checkedPos.map((val, i) => !val && i === index ? true : val),
}))
}
else if (this.state.checkedPos[index])
{
this.setState( prev => ({
checkedPos: prev.checkedPos.map((val, i) => val && i === index ? false : val),
}))
}
}
render() {
const listItems = this.state.info.map((item, index) =>
<div>
{item}
<Switch checked={this.state.checkedPos[index]}
onChange={ () => this.handleChange(index, this.state.info[index])}/>
{" Value is " + this.state.checkedPos[index]}
</div>
);
return (
<div>
{listItems}
</div>
);
}
}
render(<App />, document.getElementById('root'));
Demo here
Acording to #jose answer i've implemented to hook concept with this code
const [theArray, setTheArray] = useState([]);
const addEntry = useCallback((value) => {
setTheArray([...theArray, `${value}`]);
});
Then in function we can add value
function onChangeSwitch(isSelect,record){
console.log(isSelect); // True / False
console.log(record);
addEntry(record.no);
if(isSelect){
// push data to array
}
if(!isSelect){
// pop data from array
}
}
So when we display {enty}
<div key="adkfkdfkda">{theArray.map(entry =>
<span key={entry}>{entry},</span>
)}
</div>
We got value merged in string format
https://ibb.co/nQFK1C4
Thanks.

Resources