I have a table which displays fields of products from an API. I need the rows to appear in ASC; order. I have seen a couple methods on how to do this using React-Tables but I have it so far set up without any of the hooks so I would prefer to a built in method.
export const Table = ({data}) => {
const columns = data[0] && Object.keys(data[0])
return <table cellPadding={0} cellSpacing={0}>
<thead>
<tr>{data[0] && columns.map((heading) => <th>{heading}</th>)}</tr>
</thead>
<tbody>
{data.map(row => <tr>
{
columns.map(column => <td>{row[column]}</td>)
}
</tr>)}
</tbody>
</table>
}
you just have to sort your data array with sort()...
read this:
https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
an example here =>
Sort array by firstname (alphabetically) in Javascript
Example of a table with 3 sorting states (ascending, descending and unsorted)
const sortArray = (header) => {
const newArr = [...initailArr];
if (sortState === 0) {
newArr.sort((a, b) => {
const keyA = a[header];
const keyB = b[header];
if (keyA < keyB) return -1;
if (keyA > keyB) return 1;
return 0;
});
setSortState(1);
} else if (sortState === 1) {
newArr.sort((a, b) => {
const keyA = a[header];
const keyB = b[header];
if (keyA > keyB) return -1;
if (keyA < keyB) return 1;
return 0;
});
setSortState(2);
} else {
setSortState(0);
}
setSortedArray(newArr);
};
full working example can be found here
https://codesandbox.io/s/async-await-then-study-5yvts?file=/src/Table.js:1483-2126
Related
I have a table that has a big database, 2k lines +
So I had a function to filter and organize this working, but it takes 1-2 secs to filter, is it possible to do with useMemo or useCallBack since I always need to redo the database?
export default function Table(props) {
const [order, setOrder] = useState(true);
function handleHeaderClick(clickedHeader) {
setOrder(!order);
const newdata = props.data.sort((a, b) => {
const x = a[clickedHeader];
const y = b[clickedHeader];
if (order && x > y) return -1;
return 1;
});
props.setdata(newdata);
}
function createHeader(header, index) {
if (header.includes("BET") || header.includes("C") || header.includes("CHECK")) {
return (
<th onClick={(e) => handleHeaderClick(header)} key={index}>
{header}
</th>
);
}
}
function createRow(row) {
// ♠♥♦♣
try {
return row.replace(/s/g, "♠").replace(/d/g, "♦").replace(/c/g, "♣");
} catch (e) {
return row;
}
}
function tableRow(row, indexRow) {
return (
<tr key={indexRow}>
{Object.keys(row).map((hd) => {
const value = createRow(props.data[indexRow][hd]);
if (hd.includes("BET") || hd.includes("C") || hd.includes("CHECK")) {
return (
<td className={value[1]} key={hd}>
{value}
</td>
);
}
})}
</tr>
);
}
return (
<table>
<thead>
<tr>{Object.keys(props.data[0]).map((header, indexHeader) => createHeader(header, indexHeader))}</tr>
</thead>
<tbody>{props.data.map((row, indexRow) => tableRow(row, indexRow))}</tbody>
</table>
);
}
I think I have to change something in my handleHeaderClick function, I have already tried some solutions of this type
const test = useMemo(() => handleHeaderClick, [props.data]);
but my code stops working
where's my table
#edit violation on chrome
[Violation] 'setTimeout' handler took 1458ms
#edit2
On firefox works correct
I have a react-table with a primary header containing multiple secondary headers underneath it. When the primary header is clicked I want all but one secondary header with name 'deposit' to be hidden, or shown (toggle). See screenshot.
I have a solution using column.toggleHeader(column.isVisible). I both wanted to demonstrate this as there wasn't a lot of material out there for how to do this; and I'm wondering if there are neater solutions using .getToggleHiddenProps. To be honest I don't understand what is going on with IndeterminateCheckbox eg. https://github.com/TanStack/table/discussions/1989 and how that would be used with specific column header values.
My answer below.
Just some background - this is a Web3 app that is getting information about Crypto wallets - hence why the code looks for '0x' and reduces the column header value to const headerShortened = header.substr(0,5) + '...' + header.substr(header.length - 4,header.length) (So 0x123abcFED1239876 becomes ox123...9876)
I've just finished this initial version so don't AT me for any rough edges. Though happy to receive constructive feedback.
interface DataTableProps {
data: any
columns: Column<DripRowFormat>[]
}
interface AHeaderProps {
column: ColumnInstance<DripRowFormat>
allColumns: Array<ColumnInstance<DripRowFormat>>
}
export function DataTable({data, columns}: DataTableProps) {
const [hiddenColumns, setHiddenColumns] = useState<string[]>([])
const initialState = {hiddenColumns}
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
allColumns,
getToggleHideAllColumnsProps,
state
} = useTable({columns, data, initialState});
// console.log(`table input: ${JSON.stringify(data, null, 2)}`)
// console.log(`table rows: ${Util.inspect(rows)}`)
// console.log(`getTableProps: ${JSON.stringify(getTableProps())}`)
// console.log(`allColumns: ${Util.inspect(allColumns)}`)
const RenderHeader = ({column, allColumns}: AHeaderProps) => {
const colHeaderClick = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault()
// Now call column.toggleHidden() for all cols but 'deposit' - so when Hidden only Deposit is shown
const childColumnsNotDeposit = allColumns.filter(c =>
column.Header && c.Header && c.parent
&& c.Header.toString().toLowerCase() !== 'deposit'
&& c?.parent?.Header?.toString() === column.Header)
childColumnsNotDeposit.forEach(c => {
c.toggleHidden(c.isVisible)
})
}
const drawHeaderWRTVisibleColumns = (column: ColumnInstance<DripRowFormat>): ReactNode => {
if (! column) {
return (
<span>NOT COL</span>
)
}
const childCols = column.columns?.filter(c => c.isVisible)
// #ts-ignore
if (childCols && childCols.length < column?.columns?.length) {
// #ts-ignore
const header = column.Header.toString()
const headerShortened = header.substr(0,5) +
'...' + header.substr(header.length - 4,header.length)
return (
<span>{headerShortened}</span>
)
} else {
return column.render('Header')
}
}
// #ts-ignore
if (column.placeholderOf?.Header.startsWith('0x')) {
return (
<button onClick={colHeaderClick}>{drawHeaderWRTVisibleColumns(column)}</button>
)
} else if (column.Header?.toString().startsWith('0x')) {
return (
<button onClick={colHeaderClick}>{drawHeaderWRTVisibleColumns(column)}</button>
)
} else {
return (
<span>{column.render('Header')}</span>
)
}
}
return (
<div>
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
// #ts-ignore
<th {...column.getHeaderProps()}><RenderHeader column={column} allColumns={allColumns}/></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>
);
}
When I change a value in the table, it disappears when I update the state with the new value. I have checked state and it is updated correctly but the table is now blank. I am using a bootstrap table.
Everything in state is now updated but the grid is empty, can I use useEffect to do rebind the table?
const GridEdit = () => {
const store = useContext(StoreContext);
const handleBlur = (e, arrayRow, editableFields) => {
const newVals = store.gridItems[0][0];
newVals["Status"] = e.target.innerHTML;
store.gridItems[1](newVals);
console.log("hello ", store.gridItems[0]);
};
const dataTable = store.gridItems[0];
function tableHeaders(data) {
let tableHeaders = [];
if (data.length > 0) {
let headers = Object.keys(data[0]);
headers.map((header) =>
tableHeaders.push(<th key={header}>{header}</th>)
);
}
if (tableHeaders.length === 0) {
return null;
} else return tableHeaders;
}
function tableRows(dataTable) {
let tableLength = dataTable.length;
let table = [];
for (let i = 0; i < tableLength; i++) {
let children = [];
let row = Object.values(dataTable[i]);
const readOnlyFields = row.slice(0, 4);
const editableFields = row.slice(4, 7);
readOnlyFields.map((data) => children.push(<td id={row[0]}>{data}</td>));
editableFields.map((data) =>
children.push(
<td ContentEditable="true" id={row[0]}>
{data}
</td>
)
);
table.push(
<tr key={row} onBlur={(e) => handleBlur(e, i, editableFields)}>
{children}
</tr>
);
}
if (table.length === 0) {
return null;
} else {
return table;
}
}
return (
<tbody className="tableHeaders">
<tr>{tableHeaders(dataTable)}</tr>
{tableRows(dataTable)}
</tbody>
);
};
export default GridEdit;
Currently building hanging man game and in my case there is an array which contains 6 items. So in one round every item will be shown once of course, and also keep in mind that it is not possible to show the duplicated items in the same round. Below I was attempting to write the code that do the job, unfortunately this is not working in my case. The problem with my code is that it will keep looping through the array and when it comes to the latest index it should shuffle the array. But I get the error word is undefined if the
function shuffle
is being called
let data = ['apple', 'boring', 'citrus', 'dopamine', 'earth'];
const randomWord = () => {
setArrayCount(arrayCount + 1);
console.log(arrayCount, data.length);
if(arrayCount > data.length) {
setArrayCount(0);
shuffle(data);
} else {
}
replaceLetter(data[arrayCount].word);
cleanWord();
}
const shuffle = (a) => {
// create copy or new array
let newArr = [].concat(a);
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[newArr[i], newArr[j]] = [newArr[j], newArr[i]];
}
console.log(newArr);
return newArr;
}
#frontend. Please see my code:
const App = () => {
let data = ["apple", "boring", "citrus", "dopamine", "earth"];
const [wordArray, setWordArray] = useState([]);
const clearData = () => {
setWordArray([]);
return ["apple", "boring", "citrus", "dopamine", "earth"]
}
const randomElem = data => {
while (data.length > 0) {
const indexChoose = Math.floor(Math.random() * data.length);
wordArray.push(data[indexChoose]);
setWordArray(wordArray);
data.splice(indexChoose, 1);
}
data = clearData()
};
return (
<div className="App">
<button
onClick={() => {
randomElem(data);
console.log('result: ',wordArray);
}}
>
Change
</button>
<button
onClick={() => {
clearData();
console.log('result: ',wordArray);
}}
>
Clear
</button>
</div>
);
};
I'm trying to write a reactjs table for an assignment and am having trouble getting a sorting component working on the columns of my table. Most of this was duplicated from a previous post and I tried to integrate my data into the mold, but I am getting no results. Any idea why the code I have might not be working? Thanks!
class UserTable extends React.Component {
constructor(props){
super(props)
this.state = {
users: this.props.users,
sort: {
column: null,
direction: 'desc',
}
}
}
onSort = (column) => {
return e => {
const direction = this.state.sort.column ? (this.state.sort.direction === 'asc' ? 'desc' : 'asc') : 'desc'
const sortedUsers = this.state.users.sort((a, b) => {
if (column === 'name.first') {
const nameA = a.name.first.toUpperCase() // ignore upper and lowercase
const nameB = b.name.first.toUpperCase() // ignore upper and lowercase
if (nameA < nameB)
return -1
if (nameA > nameB)
return 1
else return 0
}
else {
return a.name.first - b.name.first
}
})
if (direction === 'desc') {
sortedUsers.reverse()
}
this.setState({
users: sortedUsers,
sort: {
column,
direction,
},
})
}
}
const setArrow = (column) => {
let className = 'sort-direction';
if (this.state.sort.column === column) {
className += this.state.sort.direction === 'asc' ? ' asc' : ' desc';
}
return className;
};
render() {
return (
<table>
<thead>
<tr>
<th onClick={this.onSort('name.first')}>
First Name
<span className={this.setArrow('name.first')}></span>
</th>
<th onClick={this.onSort('name.last')}>
Last Name
<span className={this.setArrow('name.last')}></span>
</th>
<th onClick={this.onSort('company')}>
Company
<span className={this.setArrow('company')}></span>
</th>
<th onClick={this.onSort('phone')}>
Phone
<span className={this.setArrow('phone')}></span>
</th>
<th onClick={this.onSort('email')}>
Email
<span className={this.setArrow('email')}></span>
</th>
<th onClick={this.onSort('birthdate')}>
Birthdate
<span className={this.setArrow('birthdate')}></span>
</th>
</tr>
</thead>
<tbody>
{this.state.users.map((user, index) => {
return (
<tr>
<td>{user.name.first}</td>
<td>{user.name.last}</td>
<td>{user.company}</td>
<td>{user.phone}</td>
<td>{user.email}</td>
<td>{user.birthdate}</td>
</tr>
);
})}
</tbody>
</table>
);
}
}
ReactDOM.render(<UserTable users={users} />, document.getElementById('container'));
You should return a function in onSort handler since event handlers take reference of a function but in your case they are getting undefined because you are invoking function and nothing returned.
So you code should looks like this:
onSort = column => {
return e => {
const direction = this.state.sort.column ? (this.state.sort.direction === 'asc' ? 'desc' : 'asc') : 'desc'
const sortedUsers = this.state.users.sort((a, b) => {
if (column === 'name.first') {
const nameA = a.name.first.toUpperCase() // ignore upper and lowercase
const nameB = b.name.first.toUpperCase() // ignore upper and lowercase
if (nameA < nameB)
return -1
if (nameA < nameB)
return 1
else return 0
}
else {
return a.name.first - b.name.first
}
})
if (direction === 'desc') {
sortedUsers.reverse()
}
this.setState({
users: sortedUsers,
sort: {
column,
direction,
},
})
}
}