react-table add edit/delete column - react-table

I use Rails and React-Table to display tables. It works fine so far. But How can one add an edit/delete column to the React-Table?
Is it even possible?
return (
<ReactTable
data={this.props.working_hours}
columns={columns}
defaultPageSize={50}
className="-striped -highlight"
/>
)

All you need to do is turn columns into a component state. You can see a working example https://codesandbox.io/s/0pp97jnrvv
[Updated 3/5/2018] Misunderstood the question, here's the updated answer:
const columns = [
...
{
Header: '',
Cell: row => (
<div>
<button onClick={() => handleEdit(row.original)}>Edit</button>
<button onClick={() => handleDelete(row.original)}>Delete</button>
</div>
)
}
]
where handleEdit and handleDelete will be the callbacks how you want to handle the actions when the buttons are clicked.

You can actually add buttons using the accessor prop of the columns in react-table.
here is code example:
{
Header: 'Action',
accessor: (originalRow, rowIndex) => (
<div>
<button onClick={() => handleEdit(originalRow)}>Edit</button>
<button onClick={() => handleDelete(originalRow)}>Delete</button>
</div>
),
id: 'action',
},

ReactTable v7
Add a column into a table can be achieved by inserting a column object into columns definitions that pass into useTable hook. Basically, the number of column objects that resides in the columns definitions array represents the number of columns that will be rendered by react-table.
Usually, a minimal column object consists of Header and accessor which at the Header we pass the name of the column and at the accessor, we pass the key that will be used by the react-table to look up the value from the data passed into useTable hook.
{
Header: "Column Name",
accessor: "data key", // can be a nested key
}
Here, to render other than a string inside a cell, which is, in this case, is custom button JSX, we can use either accessor or Cell option and pass to it a Function that returns a valid JSX.
accessor
The document says that accessor accept either string or Function Here we use Function to render a JSX button.
accessor: String | Function(originalRow, rowIndex) => any
One of the benefits of using accessor options is the rowIndex is directly available. The rowIndex represents the index number of rows inside the data array that is currently managed by react-table on the client-side and the originalRow is the raw object of row.
Here the rowIndex can be used as a reference to select and modify row objects in the columns data array.
Cell
Cell option accept Function that return either JSX or React.Component. Cell option is usually used for formatting a cell value, but here we use to render our button.
Cell: Function | React.Component => JSX
The Function receives tableInstance that is quite similar to the result of useTable hook with additional cell, row, and column object.
Cell: (tableInstance) => JSX
Here we can get the row index information too by destructuring the row object:
Cell: (tableInstance) => {
const { row: index } = tableInstance;
return (
...
)
}
So, it depends on your requirement in determining whether accessor or Cell will be the chosen one for rendering your edit/add button. But if you need to get more data/information from tableIntance, then Cell is the correct option to go.
Note: If you choose accessor, please make sure that the id is included in the column properties due to the id option being required as the document said so.
Required if accessor is a function.
This is the unique ID for the column. It is used by reference in things like sorting, grouping, filtering etc.
Now, we already have the column. The next is the button. Commonly the button is either a normal button that will either call a handler for updating a state or triggering a dialog popup or a link button that will redirect the app to the detail page.
So, the code will be:
// accessor
{
Header: 'Action',
id: 'action',
accessor: (originalRow, rowIndex) => {
return (
// you can pass any information you need as argument
<button onClick={() => onClickHandler(args)}>
X
</button>
)
}
}
// or Cell
{
Header: 'Action',
accessor: "action",
Cell: (tableInstance) => {
const { row: index } = tableInstance;
return (
// you can pass any information you need as argument
<button onClick={() => onClickHandler(args)}>
X
</button>
)
}
}
Example:
const { useCallback, useEffect, useMemo, useState } = React;
const { useTable } = ReactTable;
// table data
const data = [
{
name: "John",
workingHours: 40
},
{
name: "Doe",
workingHours: 40
}
];
const AddEmployee = ({ onSubmit }) => {
const [name, setName] = useState("");
const [workingHours, setWorkingHours] = useState("");
const handleSubmit = (e) => {
onSubmit(e);
setName("");
setWorkingHours("");
}
return (
<fieldset style={{ width: "200px" }}>
<legend>Add Employee:</legend>
<form onSubmit={(e) => handleSubmit(e)}>
<input
type="text"
name="name"
placeholder="Name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<br />
<input
type="text"
name="workingHours"
placeholder="Working Hours"
value={workingHours}
onChange={(e) => setWorkingHours(e.target.value)}
/>
<br />
<button type="submit">Add</button>
</form>
</fieldset>
)
}
const EditEmployee = ({ row, onSave }) => {
const { originalRow, rowIndex } = row;
const [name, setName] = useState(originalRow.name);
const [workingHours, setWorkingHours] = useState(originalRow.workingHours);
return (
<fieldset style={{ width: "200px" }}>
<legend>Edit Employee:</legend>
<input
type="text"
name="name"
placeholder="Name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<br />
<input
type="text"
name="workingHours"
placeholder="Working Hours"
value={workingHours}
onChange={(e) => setWorkingHours(e.target.value)}
/>
<br />
<button onClick={() => onSave({ name, workingHours }, rowIndex)}>Save</button>
</fieldset>
)
}
function App() {
const [tableData, setTableData] = useState(data);
const [editingRow, setEditingRow] = useState();
const handleDelete = useCallback((index) => {
setTableData(tableData.filter((v, i) => i !== index));
},[tableData]);
const tableColumns = useMemo(() => [
{
Header: 'Name',
accessor: 'name',
},
{
Header: 'Working Hours',
accessor: 'workingHours'
},
{
Header: 'Action',
id: 'action',
accessor: (originalRow, rowIndex) => {
return (
<div>
<button onClick={() => setEditingRow({ originalRow, rowIndex })}>
Edit
</button>
<button onClick={() => handleDelete(rowIndex)}>
Delete
</button>
</div>
)
}
}
], [handleDelete]);
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow
} = useTable({
columns: tableColumns,
data: tableData,
});
const handleSubmit = (event) => {
event.preventDefault();
const formData = new FormData(event.currentTarget);
const newData = {};
formData.forEach((value, key) => newData[key] = value);
setTableData((prevData) => {
return [...prevData, newData];
});
};
const handleEdit = useCallback((row, rowIndex) => {
const editedData = tableData.map((rowData, index) => {
if (index === rowIndex) {
return row;
}
return rowData;
});
setTableData(editedData);
setEditingRow();
},[tableData])
return (
<div>
<h3>React-table v.7</h3>
<br />
{ editingRow ? <EditEmployee row={editingRow} onSave={handleEdit} /> : <AddEmployee onSubmit={handleSubmit} /> }
<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.map((row, i) => {
prepareRow(row)
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return (
<td {...cell.getCellProps()}>
{cell.render('Cell')}
</td>
)
})}
</tr>
)
})}
</tbody>
</table>
</div>
)
}
ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/react-table#7.8.0/dist/react-table.development.js"></script>
<div class='react'></div>

Related

Array value not updating in React component

I am trying to filter data based on set of checkboxes and display the updated data in my NextJs project. The data to be displayed is fetched from api(cspaces). "filteredList" is the filtered data which is being passed to the component.
Filter function:
let filteredList = cspaces;
const onFilterChange = (event, filter, rorb) => {
event.preventDefault();
if ( activeFilterPType.includes(filter)) {
const filterIndex = activeFilterPType.indexOf(filter);
const newFilter = [...activeFilterPType];
newFilter.splice(filterIndex, 1);
setActiveFilterPType(newFilter);
} else {
setActiveFilterPType([...activeFilterPType, filter]);
}
if (
activeFilterPType.length === 0 ||
activeFilterPType.length === filterList.length
) {
filteredList = cspaces;
// filteredList = cspaces.filter(item => item.attributes.leadType.includes(rorb))
}
else {
let reg1 = /Shop|Office|Showroom/
filteredList = cspaces.filter(item =>
activeFilterPType.includes((item.attributes.propertySubType.match(reg1))["0"])
)
filteredList = filteredList.filter(item => item.attributes.leadType.includes(rorb)
)
console.log('filteredlist -> ',filteredList)
}
}
The checkboxes:
...
{data.propertytype.items.map((tag,index) => (
<li key={tag.value}>
<Form.Check
type="checkbox"
id={tag.value}
name={tag.value}
label={tag.label}
value={tag.label}
onChange={(e)=> {handleCheckboxChange(e,tag.label)}}
/>
....
The handleCheckboxChange function:
...
const handleCheckboxChange = (e,value) => {
if(e.target.checked) setPType(value)
}
...
The component where I am passing filtered data is :
....
<ResultsTopBar data={filteredList} sortBy={data.sortby} sortData={sortTheKey}/>
{sortKey==='latest'?
filteredList && (
<Row>
{filteredList.map((filteredListItems, id) => (
<Col key={id} sm="6" lg="4" className="mb-5 hover-animate">
<CardCspace data={filteredListItems} />
</Col>
)
)}
</Row>
)
.....
Button on click the onFilterChange function is called:
<Button type="submit" onClick={(e) => onFilterChange(e, pType, rorb)}>
<FontAwesomeIcon icon={faFilter} className="me-1" />
Filter
</Button>
The data is getting filtered n displayed in console, but in web page shows all of the original response data without filtering. Please help!
Snapshot is below:
filteredList

React table maximum update depth exceeded

I'm trying to render a table of data fetch from an API.
The first render is fine, everything seems to be ok, but when i'm trying to use SortBy or when i'm trying to add an input that i could use to call filtered data to the API i got the error :
react-dom.development.js:27292 Uncaught Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
at checkForNestedUpdates (react-dom.development.js:27292:1)
at scheduleUpdateOnFiber (react-dom.development.js:25475:1)
at dispatchReducerAction (react-dom.development.js:17452:1)
at react-table.development.js:944:1
at react-table.development.js:253:1
at commitHookEffectListMount (react-dom.development.js:23150:1)
at commitLayoutEffectOnFiber (react-dom.development.js:23268:1)
at commitLayoutMountEffects_complete (react-dom.development.js:24688:1)
at commitLayoutEffects_begin (react-dom.development.js:24674:1)
at commitLayoutEffects (react-dom.development.js:24612:1)
const TableAnime = () => {
const [animes, setAnimes] = useState({});
const [loadingData, setLoadingData] = useState(true);
const [endpoint, setEndpoint] = useState('/anime?page[limit]=10&page[offset]=0')
const getAnimes = async () => {
const response = await axios.get(`https://kitsu.io/api/edge${endpoint}`);
setAnimes(response.data);
setLoadingData(false);
}
useEffect(() => {
getAnimes()
}, []);
const handleForm = async () => {
const results = await axios.get(`https://kitsu.io/api/edge/anime`);
setAnimes(results.data)
console.log(animes)
}
const data = useMemo(() => (animes.data), [animes.data]);
return (
<div>
<Form handleForm={handleForm}/>
<h1>Catalogue</h1>
{loadingData ?
(
<p>Loading Please wait...</p>
) : (
<Table animes={animes.data} />
)
}
</div>
);
}
// == Export
export default TableAnime;
Table.js :
// == Import
import { useTable } from "react-table";
import { useMemo } from "react";
import { Link } from 'react-router-dom';
import moment from "moment"
import RowItem from "./RowItem";
import HeaderItem from "./HeaderItem";
// == Composant
const Table = ({animes}) => {
const columns = useMemo(() => [
{ Header: "Titre", accessor: "attributes.canonicalTitle"},
{ Header: "Titre Japonais", accessor: "attributes.titles.ja_jp"},
{ Header: "Age recommandé", accessor: "attributes.ageRatingGuide"},
{ Header: "Date de sortie", accessor: d => moment(d.attributes.startDate).format("DD/MM/YYYY")},
{ Header: "Rang", accessor: "attributes.popularityRank"},
{ Header: " ", accessor: d => <Link to={`/anime/${d.id}`}>Voir les détails</Link>},
]);
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
} = useTable({columns, data: animes})
return (
<>
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<HeaderItem headerGroup={headerGroup} key={Date.now()}/>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map(row => {
prepareRow(row)
return (
<RowItem key={(Date.now()*Math.random())} row={row} />
)
})}
</tbody>
</table>
</>
);
}
// == Export
export default Table;
Then i got RowItem and HeaderItem :
const RowItem = ({row}) => {
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return (
<td {...cell.getCellProps() }>
{cell.render('Cell')}
</td>
)
})}
</tr>
);
}
export default RowItem
const HeaderItem = ({headerGroup}) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps()} >
{column.render('Header')}
</th>
))}
</tr>
);
export default HeaderItem ;
From what i understand that could be because i didn't use useMemo properly but i don't know how to do that. I tried at multiple places but nothing worked.
I'm using React Table for the first time and i'm new to React, so I'm sorry if the answer seems really easy but i really coudn't figured it out myself.
You need to cache the getAnime fetcher function by using useCallback and pass the endpoint as the dependency to make sure the fetcher fn is only called once (twice in strict mode).
So, change this:
const getAnimes = async () => {
const response = await axios.get(`https://kitsu.io/api/edge${endpoint}`);
setAnimes(response.data);
setLoadingData(false);
}
useEffect(() => {
getAnimes()
}, []);
into this:
const getAnimes = useCallback(async () => {
const response = await axios.get(`https://kitsu.io/api/edge${endpoint}`);
setAnimes(response.data);
setLoadingData(false);
}, [endpoint]);
useEffect(() => {
getAnimes();
}, [getAnimes]);
You can check here

Can you stop the label from outputting for a custom input with formkit?

I am creating a custom input via the component method with formkit.
I have the computed which looks like this
<template>
<label
class="InputCheckboxFeature"
:for="props.context.id"
>
<input
:id="props.context.id"
type="checkbox"
class="sr-only"
:checked="checked"
#input="handleInput"
:value="props.context._value"
/>
<span class="InputCheckboxFeature__indicator">
<span class="space-x-4 inline-flex items-center">
<span
v-if="props.context.attrs.icon"
class="InputCheckboxFeature__icon"
v-html="props.context.attrs.icon"
>
</span>
<span class="inline-block">
{{ props.context.label }}
</span>
</span>
<ButtonInfo
v-if="props.context.tooltip"
class="flex-shrink-0"
:text="props.context.tooltip"
aria-label="More Info"
/>
</span>
</label>
</template>
I then have a plugin that strips out all the HTML elements so the custom input will only output the component markup
export const createCleanInputPlugin = (): FormKitPlugin => {
return (node: FormKitNode) => {
node.on('created', () => {
const { props } = node;
if (
!(
props.type === InputType.FEATURED_CHECKBOX &&
props.definition &&
typeof props.definition.schema === 'function'
)
) {
return;
}
const definition = { ...props.definition };
const schema = definition.schema as FormKitExtendableSchemaRoot;
// We replace the schema function with our own higher-order-function
definition.schema = function (extensions = {}) {
const ext = {
...extensions,
...{
inner: { $el: null },
outer: { $el: null },
label: { $el: null },
wrapper: { $el: null },
},
};
// Finally we call the original schema, with our extensions applied
return schema(ext);
};
// Now we replace the input definition
props.definition = definition as FormKitTypeDefinition;
});
};
};
The problem I am facing is that when the element is rendered it is outputting the label twice.
Is it possible to hide the label, and only have it render in the component?

React-Redux how to save state

I'm having some trouble figuring out how to save the state in my app. I have a component where a user adds a 'NAME' and a 'WEIGHT'. When the user clicks the submit button, it redirects them to the Home Page and the newly added name is displayed (the weight will be displayed elsewhere).
What I'm having trouble with is when I go back and add another 'NAME' and 'WEIGHT', the previous name disappears and is replaced with the new one. What I would like to happen is have the previous 'NAME' stay on the Home Page when I add a new one.
Here is my AddPage component:
const AddPage = () => {
const [name, setName] = useState('');
const [weight, setWeight] = useState(0);
const classes = useStyles();
const dispatch = useDispatch();
return (
<div>
<Header title="Add Page" />
<div className={classes.addPage}>
<div className={classes.addMovementDiv}>
<TextField
className={classes.movementName}
key="name"
label="Enter Movement Name"
InputProps= {{className: "textBoxColor"}}
variant="outlined"
onChange={event => {
const { value } = event.target;
setName(value);
}}
/>
<TextField
className={classes.movementWeight}
key="weight"
label="Enter Movement Weight"
type="number"
variant="outlined"
onChange={event => {
const { value } = event.target;
setWeight(value);
}}
InputProps= {{endAdornment: <InputAdornment position="end">lb</InputAdornment>, className: "textBoxColor"}} />
<Button
className={classes.addButton}
variant="outlined"
onClick={() => dispatch(addMovement(name, weight))}
>
<AddCircleIcon />
</Button>
</div>
</div>
</div>
);
};
const mapStateToProps = (state) => {
return {
name: state.move.name,
weight: state.move.weight,
}
};
const mapDispatchToProps = (dispatch) => {
return({
addMovement: (name, weight) => dispatch(addMovement(name, weight)),
})
};
const withConnect = connect(
mapStateToProps,
mapDispatchToProps,
);
export default compose(withConnect)(AddPage);
Here is my HomePage component:
const HomePage = () => {
const classes = useStyles();
const name = useSelector(state => state.move.name);
const displayMovementButtons = () => {
if (name) {
return (
<Button
className={classes.movementButtons}
onClick={() => history.push('/movement/:id')}
>
<div className={classes.movementName} >{name}</div>
</Button>
)
}
return <div className={classes.noMovementsMessage} >Click add button to begin</div>
}
return (
<div className={classes.homePageContent} >
<Header title={"Home Page" }/>
<div>{displayMovementButtons()}</div>
<div className={classes.fabDiv}>
<Fab
className={classes.fab}
onClick={() => history.push(`/add`)}>
<AddIcon />
</Fab>
</div>
</div>
);
};
const mapStateToProps = (state) => {
return {
name: state.move.name,
}
};
const withConnect = connect(
mapStateToProps,
);
export default compose(withConnect)(HomePage);
Here is my reducer:
const initialState = []
const addMovementReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_MOVEMENT:
return [ ...state, {name: action.name, weight: action.weight} ]
default:
return state;
}
};
export default addMovementReducer;
Here is where my store is set up:
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
rootReducer,
composeEnhancers(applyMiddleware(reduxThunk))
);
ReactDOM.render(
<Provider store={store} >
<App />
</Provider>,
document.querySelector('#root')
);
Any help would be appreciated!
you're misusing redux state by using it along side with hooks. it's an either or situation
may I see your store and reducer ?

changing Material-ui table style in row.getRowProps()

In my react application I'm using Material-UI enhanced table which is based on react-table and I would like to change the style of their rows.
Reading from documentation (https://material-ui.com/api/table-row/) in the component TableRow should be used the prop "classes" to change the style, but in the MaterialUi code props are read this way:
<TableRow {...row.getRowProps()}>
My question is how can I use the prop classes if the TableRow props are added automatically? I thought I needed to have this:
<TableRow classes="rowStyle ">
where rowStyle is:
const styles = {
rowStyle : {
padding: 10,
border: "1px solid red"
}
};
But obviously I can't this way, how can I add "classes" to the getRowProps() and the new style in it?
I couldn't find an explanation or a good example in official documentation or stackOverflow
Many thanks for the help
EnhancedTable.js:
import React from "react";
import Checkbox from "#material-ui/core/Checkbox";
import MaUTable from "#material-ui/core/Table";
import PropTypes from "prop-types";
import TableBody from "#material-ui/core/TableBody";
import TableCell from "#material-ui/core/TableCell";
import TableContainer from "#material-ui/core/TableContainer";
import TableFooter from "#material-ui/core/TableFooter";
import TableHead from "#material-ui/core/TableHead";
import TablePagination from "#material-ui/core/TablePagination";
import TablePaginationActions from "./TablePaginationActions";
import TableRow from "#material-ui/core/TableRow";
import TableSortLabel from "#material-ui/core/TableSortLabel";
import TableToolbar from "./TableToolbar";
import {
useGlobalFilter,
usePagination,
useRowSelect,
useSortBy,
useTable,
} from "react-table";
const IndeterminateCheckbox = React.forwardRef(
({ indeterminate, ...rest }, ref) => {
const defaultRef = React.useRef();
const resolvedRef = ref || defaultRef;
React.useEffect(() => {
resolvedRef.current.indeterminate = indeterminate;
}, [resolvedRef, indeterminate]);
return (
<div>
<Checkbox ref={resolvedRef} {...rest} />
</div>
);
}
);
const inputStyle = {
padding: 0,
margin: 0,
border: 0,
background: "transparent",
};
// Create an editable cell renderer
const EditableCell = ({
value: initialValue,
row: { index },
column: { id },
updateMyData, // This is a custom function that we supplied to our table instance
}) => {
// We need to keep and update the state of the cell normally
const [value, setValue] = React.useState(initialValue);
const onChange = (e) => {
setValue(e.target.value);
};
// We'll only update the external data when the input is blurred
const onBlur = () => {
updateMyData(index, id, value);
};
// If the initialValue is changed externall, sync it up with our state
React.useEffect(() => {
setValue(initialValue);
}, [initialValue]);
return (
<input
style={inputStyle}
value={value}
onChange={onChange}
onBlur={onBlur}
/>
);
};
EditableCell.propTypes = {
cell: PropTypes.shape({
value: PropTypes.any.isRequired,
}),
row: PropTypes.shape({
index: PropTypes.number.isRequired,
}),
column: PropTypes.shape({
id: PropTypes.number.isRequired,
}),
updateMyData: PropTypes.func.isRequired,
};
// Set our editable cell renderer as the default Cell renderer
const defaultColumn = {
Cell: EditableCell,
};
const EnhancedTable = ({
columns,
data,
setData,
updateMyData,
skipPageReset,
}) => {
const {
getTableProps,
headerGroups,
prepareRow,
page,
gotoPage,
setPageSize,
preGlobalFilteredRows,
setGlobalFilter,
state: { pageIndex, pageSize, selectedRowIds, globalFilter },
} = useTable(
{
columns,
data,
defaultColumn,
autoResetPage: !skipPageReset,
// updateMyData isn't part of the API, but
// anything we put into these options will
// automatically be available on the instance.
// That way we can call this function from our
// cell renderer!
updateMyData,
},
useGlobalFilter,
useSortBy,
usePagination,
useRowSelect,
(hooks) => {
hooks.allColumns.push((columns) => [
// Let's make a column for selection
{
id: "selection",
// The header can use the table's getToggleAllRowsSelectedProps method
// to render a checkbox. Pagination is a problem since this will select all
// rows even though not all rows are on the current page. The solution should
// be server side pagination. For one, the clients should not download all
// rows in most cases. The client should only download data for the current page.
// In that case, getToggleAllRowsSelectedProps works fine.
Header: ({ getToggleAllRowsSelectedProps }) => (
<div>
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
</div>
),
// The cell can use the individual row's getToggleRowSelectedProps method
// to the render a checkbox
Cell: ({ row }) => (
<div>
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
</div>
),
},
...columns,
]);
}
);
const handleChangePage = (event, newPage) => {
gotoPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setPageSize(Number(event.target.value));
};
const removeByIndexs = (array, indexs) =>
array.filter((_, i) => !indexs.includes(i));
const deleteUserHandler = (event) => {
const newData = removeByIndexs(
data,
Object.keys(selectedRowIds).map((x) => parseInt(x, 10))
);
setData(newData);
};
const addUserHandler = (user) => {
const newData = data.concat([user]);
setData(newData);
};
// Render the UI for your table
return (
<TableContainer>
<TableToolbar
numSelected={Object.keys(selectedRowIds).length}
deleteUserHandler={deleteUserHandler}
addUserHandler={addUserHandler}
preGlobalFilteredRows={preGlobalFilteredRows}
setGlobalFilter={setGlobalFilter}
globalFilter={globalFilter}
/>
<MaUTable {...getTableProps()}>
<TableHead>
{headerGroups.map((headerGroup) => (
<TableRow {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<TableCell
{...(column.id === "selection"
? column.getHeaderProps()
: column.getHeaderProps(column.getSortByToggleProps()))}
>
{column.render("Header")}
{column.id !== "selection" ? (
<TableSortLabel
active={column.isSorted}
// react-table has a unsorted state which is not treated here
direction={column.isSortedDesc ? "desc" : "asc"}
/>
) : null}
</TableCell>
))}
</TableRow>
))}
</TableHead>
<TableBody>
{page.map((row, i) => {
prepareRow(row);
return (
<TableRow {...row.getRowProps()}>
{row.cells.map((cell) => {
return (
<TableCell {...cell.getCellProps()}>
{cell.render("Cell")}
</TableCell>
);
})}
</TableRow>
);
})}
</TableBody>
<TableFooter>
<TableRow>
<TablePagination
rowsPerPageOptions={[
5,
10,
25,
{ label: "All", value: data.length },
]}
colSpan={3}
count={data.length}
rowsPerPage={pageSize}
page={pageIndex}
SelectProps={{
inputProps: { "aria-label": "rows per page" },
native: true,
}}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
ActionsComponent={TablePaginationActions}
/>
</TableRow>
</TableFooter>
</MaUTable>
</TableContainer>
);
};
EnhancedTable.propTypes = {
columns: PropTypes.array.isRequired,
data: PropTypes.array.isRequired,
updateMyData: PropTypes.func.isRequired,
setData: PropTypes.func.isRequired,
skipPageReset: PropTypes.bool.isRequired,
};
export default EnhancedTable;
Correct me if i'm wrong, but i think the first step to your solution is the correct definition of styles for the material-ui element. I can't say for certain if there is another way to generate these styles, but a simple object such as:
const inputStyle = { padding: 0, margin: 0, border: 0, background: "transparent", };
will probably not work. You probably have to use the material styles for that.
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles({
root: {
border: "1px solid red",
padding: 10
},
});
and then you have to use the style hook in the component definition:
const classes = useStyles();
When overriding a material-ui definition this part is the important one to define which part is going to overriden (not allowed to include pictures yet, sorry):
Material-Ui CSS keys for Table Row
Then you can override the style <TableRow classes={{ root: classes.root }}> or in your case maybe more like <TableRow classes={{ root: classes.root }} {...row.getRowProps()}>
An additional problem you might face is that you have to override the style of the TableCells too, because the overlap the TableRow border.
I have here a code sandbox that is by no means perfect, but should help you on the right track: https://codesandbox.io/s/material-demo-535zq?file=/demo.js
import { makeStyles } from '#material-ui/core/styles';
const useStyles = makeStyles(() => ({
rowStyle : {
padding: 10,
border: "1px solid red"
}
}));
const EnhancedTable = ()=>{
const classes = useStyles();
return(
<TableRow className={classes.rowStyle}/>
)
}

Resources