React Table using hooks expand and collapse rows - reactjs

I am using react-table component inside my project. The row expansion property is something that my features utilized and it is working fine now.
I need the ability to collapse all the rows while I expand a row. ie Only one row should be open at a time. I did go through many documentation and stackoverflow links but none didn't work out. Please note that this implementation is using hooks. Just like the one mentioned here : https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/expanding
By default they allow to open more than one row at a time, but I need to implement the opposite.
The closest I came to is this : Auto expandable rows and subrows react table using hooks
But here its opening on initial load.
Thanks

I have only added a portion of App function here. Codesandbox: https://codesandbox.io/s/jolly-payne-dxs1d?fontsize=14&hidenavigation=1&theme=dark.
Note: I am not used to react-table library. This code is a sample that only works in the table with two levels of rows. You can optimize the code with recursion or some other way to make the code work in multi-level tables.
Cell: ({ row, rows, toggleRowExpanded }) =>
// Use the row.canExpand and row.getToggleRowExpandedProps prop getter
// to build the toggle for expanding a row
row.canExpand ? (
<span
{...row.getToggleRowExpandedProps({
style: {
// We can even use the row.depth property
// and paddingLeft to indicate the depth
// of the row
paddingLeft: `${row.depth * 2}rem`
},
onClick: () => {
const expandedRow = rows.find(row => row.isExpanded);
if (expandedRow) {
const isSubItemOfRow = Boolean(
expandedRow && row.id.split(".")[0] === expandedRow.id
);
if (isSubItemOfRow) {
const expandedSubItem = expandedRow.subRows.find(
subRow => subRow.isExpanded
);
if (expandedSubItem) {
const isClickedOnExpandedSubItem =
expandedSubItem.id === row.id;
if (!isClickedOnExpandedSubItem) {
toggleRowExpanded(expandedSubItem.id, false);
}
}
} else {
toggleRowExpanded(expandedRow.id, false);
}
}
row.toggleRowExpanded();
}
})}
>
{row.isExpanded ? "👇" : "👉"}
</span>
) : null

Related

React Table responsive hidden columns

I'm trying to find a way to hide a few columns based on the window size using react-table.
I've gotten as far as using a custom hook to return the window size, then based on those size's to return an array with the column accessor id's
const { width } = useWindowDimensions();
const [cols, setCols] = useState([""]);
.
.
. useEffect(() => {
console.log("cols", cols);
if (width < 1000) {
setCols(["id", "osName"]);
} else {
setCols(["id"]);
}
}, [width, setCols]);
.
.
. <ComputerTable
hiddenColumns={cols}
columns={columns}
data={...}
};
})}
/>
this passes through my Table component and is loaded on
useTable(
.
.
.
initialState: { pageIndex: 0, hiddenColumns: hiddenColumns || [] },
.
.
)
This works on the initial page render, however when the window resizes it does not update.
cols does appear to be updating on the fly within the component code when I console.log it, but in the view itself, the table does not re-render based on that array update.
Can I accomplish this with my logic, or is there a better way to accomplish this?
initialState: { pageIndex: 0, hiddenColumns: hiddenColumns || [] }, is an initial state, not an updating one. You will need to engineer things so you can say:
colsToHide.forEach(c => c.toggleHidden(true))
// OR
colsToHide.forEach(c => c.toggleHidden(c.isVisible)) // to toggle state
// OF
colsToHide.forEach(c => c.toggleHidden(state.blah)) // for if you setup some state
Use an useEffect() to perform state setup as required, and in the return (...) for rendering the table.
I am about to write up what I did in a new post to answer how to hide columns NOT using
<IndeterminateCheckbox {...getToggleHideAllColumnsProps()}/>
which I could only find answers for.
See React-table hide various columns from button click

How do i make a component like this in react?

What I needed was something like that shown in the picture. I need to show certain names and if the names list exceeds more than 2 rows I need to show +n others. If the user clicks +n others the list needs to be expanded to show all the others.
Is there any component available in react to get this result? I have seen it on a number of websites but don't know what they are called.
I could write the component myself but the difficult part would be how many names to show before i show the +n others. I can only show 2 rows initially and each name can be of variable length. So in one case, a single name may take up the entire 1st row and in others, i may be able to fit 3 names.
You have to store the state of the list see it's expanded or not.
Something like this should help.
import React, { useEffect, useState } from "react";
const myList = ({ list }) => {
const MAX_COUNT = 5;
const [isExpended, setIsExpended] = useState(false);
useEffect(() => {
if (list.length <= MAX_COUNT) {
setIsExpended(true);
}
}, [list]);
const expend = () => {
setIsExpended(true);
};
return (
<>
<div>
{list.map((item, i) =>
(isExpended === false && i < MAX_COUNT) || isExpended === true
? item.text
: null
)}
</div>
{isExpended === false ? (
<button onClick={expend}>+ {list.length - MAX_COUNT} others</button>
) : null}
</>
);
};
export default myList;
If you want to stick with 2 rows on any conditions there will be 2 approach to set the dynamic MAX_COUNT:
1: if you have a constant value for box-sizing and fonts and etc:
You can compute outerWidth of each elements (with box-sizing and elements length) and set a real MAX_COUNT based on that.
2: if you have responsive design:
you can render component with initial MAX_COUNT but hide contents with visibility: hidden and then computing outerWidth of each elements would be more realistic and much more easier.
in this scenario you have to fix the container height to prevent it from extending too much, just set a constant height to it, also you can change it after you get a real MAX_COUNT. now you can show contents with no worries.

How to fix pagination reset issue in react-table?

I am working for a feature where i have to apply a filter outside the component which is using react-table, but the current page number doesn't get reset after the filter is being applied. The result being fetched (have applied server-side pagination) shows the first page's data.
I have tried to use the callback onFetchData to change the current page number but it does'nt get triggered when the filter from outside the component is applied.
render() {
const { orders, onUpdate } = this.props;
const defaultPageSize = 50;
return (
<div className="admin-report-table">
<ReactTable
data={orders.ordersData}
columns={this.columns}
pages={Math.ceil(orders.count / defaultPageSize)}
defaultPageSize={defaultPageSize}
multiSort={false}
showPageSizeOptions={false}
showPaginationTop
className="-striped -highlight"
noDataText="No orders found. Please check your connection or change your filters."
filterable
manual // informs React Table that you'll be handling sorting and pagination server-side
onFetchData={(state) => {
const { page, pageSize, filtered, sorted } = state;
const adjustedPage = page + 1; // since library page starts from 0
onUpdate({
page: adjustedPage,
filtered,
pageSize,
sorted,
});
}}
/>
</div>
);
}
The Page number should be reset to 1 e.g. current display is Page 2 of 3, after the filter from outside the table is applied, the result is fetched and shown but the Page 2 of 3 doesn't change while the result in the table is of page 1.
Set autoResetPage: false
https://react-table.tanstack.com/docs/api/usePagination#table-options
its gonna avoid rendering all the time.
I had the same issue in my react table, in the solution for that, in the componentWillReceiveProps method I update the page props of the react-table and set it to the "0" when react-table gets new filtered data (Props changed ), its work for me fine, so in your code just set page props as 0 when your table get new data from the parent
componentWillReceiveProps(nextProps: Readonly<IProps>): void {
if (nextProps.properties !== this.props.properties && nextProps.properties.length > 0) {
this.setState({
selectedPageNumber : 0
})
}
}
This issue could be solved by the hacky solution found here: TroyWolf's solution.
And read the whole post there - it may be that your current case is in another comment. If the post will be removed somehow I post it here:
store a reference to the ReactTable's instance. It works to directly update myReactTableInstance.state.page = 0
With similar use cases, I have found its best to use once's own controlled page state for index, as mentioned here:
Check here as mentioned in the docs
It becomes simpler to handle the page reset actions on custom filtering/custom sorting, and trigger accordingly
I am using react table's out of the box pagination and the issue I had was how to extract the current pageIndex from react table. Because every time there was an action on the row of React table, that did not alter the no. of rows, the React table would go back to page 0.
Following worked for me, hacky but works -
On parent component calling React table -
signalTableData is where I have my data for columns and datarows. this could be your data.
const [pageIndex, setPageIndex] = useState(0);
<ReactTable
columns={signalTableData ? signalTableData.headerRow : <div>Loading...</div>}
data={signalTableData.dataRows ? signalTableData.dataRows : <div>Loading...</div>}
sortbyCollumn='originDate'
desc={true}
pageIndex = {pageIndex}
setPageIndex = {setPageIndex}
/>
One React Table js -
function Table({ columns, data, sortbyCollumn, desc, pageIndex, setPageIndex }) {...
and
useTable(
{
columns,
data,
defaultColumn, // Be sure to pass the defaultColumn option
filterTypes,
autoResetPage: false,
initialState: {
pageSize: 5,
pageIndex: pageIndex,
sortBy: [
{
id: sortbyCollumn,
desc: desc
}
] },
},

React components get duplicated on resize/re-render even though they have unique keys

Can you please advise what is missing in my code (https://codesandbox.io/s/x2q89v613o) that causes copies of components to be created on resize even though I had assigned unique keys to them?
Project is simple scheduler table with each cell being a component and event is also component. Some complexity added by using React Drag and Drop .. could it be that using HOC wrapper makes React do not recognize existing elements?
Thanks!!
VB
Add your componentWillReceiveProps with below in WeekView, I have added a line cell.events= [];. This is clear all previous events and add the new which came in props.
componentWillReceiveProps(nextProps) {
if (nextProps.events && nextProps.events.length) {
console.log("weekView componentWillReceiveProp got events");
this.state.cells.forEach(cell=>{
cell.events= [];
});
nextProps.events.forEach(x => {
const start = x.start;
const cellId = "c" + "_" + moment(start).valueOf();
const target = Helper.getItemFromArray(this.state.cells, cellId, "id");
if (target) {
console.log("found");
target.events.push(x);
}
});
console.log(
this.state.cells.filter(x => {
if (x.events.length > 0) return x;
})
);
}
}
The below line is causing the problem
tableWidth: ReactDOM.findDOMNode(this._tableTarget).offsetWidth
offsetWidth is changing on screen size change and hence creating a copy.
Remove it and try.
tableWidth: "auto"
or
tableWidth: "100%"

How to rerender entire single row in react-table?

I have a react-table component in my app and pass there next props:
<ReactTable
data={this.props.words}
columns={columns}
getTrProps={changing_state_func}
/>
changing_state_func is a function that changes state of the component that wraps react-table. Specifically, this function fills an array that contains ids of the data objects for rows. So I'd like to highlight the rows with the data for those ids... Cannot figure it out how to do it with this particular datagrid. Maybe somebody has the experience working with react-table and example on this case.
I'm not sure what actually you're trying to do, but here are my thoughts.
getTrProps is used to pass props to the table's rows.
So according the module docs, you can pass whatever you want.
The example shows how you can change the color of the rows, those meet specific condition:
// Any Tr element will be green if its (row.age > 20)
<ReactTable
getTrProps={(state, rowInfo, column) => {
return {
style: {
background: rowInfo.age > 20 ? 'green' : 'red'
}
}
}}
/>
Fixes to the react-table 5.0.2 allow to have access to the table's instance, and the problem can be solved by calling .forceUpdate() method on the table's instance. changing_state_func in this case may look like this:
changing_state_func = (state, rowInfo, column, instance) => {
if (!rowInfo) return {}
return {
style: {
background: (this.state.rowsIdsArray.includes(rowInfo.row.id)) ? 'green' : null
},
onClick: () => {
this.setState({rowsIdsArray: this.state.rowsIdsArray.concat([rowInfo.row.id])});
instance.forceUpdate()
}
}
}
Thanks to the Tanner Linsley for responsiveness and quick fixes.

Resources