I am trying to make react table scrollable horizontally and vertically but right now it is taking all the space according to the data.
I want to make this table of fixed height and scrollable in both direction.
Table component:
import React from "react";
import { useTable } from "react-table";
const Table = ({ columns, data }) => {
const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } =
useTable({
columns,
data,
});
return (
<table {...getTableProps()} className="text-center">
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th
className="bg-primary-dark text-white p-4 text-center"
{...column.getHeaderProps()}
>
{column.render("Header")}
</th>
))}
</tr>
))}
</thead>
<tbody
{...getTableBodyProps()}
className="bg-primary-light text-primary-dark overflow-scroll"
>
{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>
);
};
export default Table;
Table Container
import React, { useMemo } from "react";
import useData from "../../hooks/useData";
import Table from "./Table";
const TableSection = React.memo(({ query }) => {
const { data, runtime, error } = useData(query);
const column =
data.length > 0 &&
Object.keys(data[0]).map((key) => {
return {
Header: data[0][key],
accessor: key,
};
});
const columns = useMemo(() => column, [column]);
const queryData = useMemo(() => data.slice(1), [data]);
return (
<div className="col-start-2 col-end-3 row-start-3 row-end-4 text-white m-6">
<Table columns={columns} data={queryData} />
</div>
);
});
export default TableSection;
Anyone please help me with this.
I know is late but others might be searching also for this, as I found no answers this worked pretty well; actually is simple, wrap always your React Table on a div and a className, then on CSS file do this:
.tableContainer{
background-color: #1E1F25;
border-radius: 20px;
height: 244px;
padding: 0 10px 10px 10px;
overflow: scroll;
}
The key is to have a fixed height for the div that is wrapping the table and overflow set to scroll. Also, you can keep the table header fixed by doing this:
Table thead{
position: sticky;
top: 0px;
margin: 0 0 0 0;
}
Define the style on the container you want to be fixed-height and scrollable.
maxHeight: "30rem",
overflow: "auto",
Related
I have a table where I am feeding data into it from an api, the data is fetched correctly and displays properly for the table row and data which is part of the tbody but inside the thead I try to display a header but nothing seems to be displayed even though the data being passed to the header is being logged to the console. Can anyone see my issue?
Component with table:
import React, {useState, useEffect} from 'react';
import secondsToMinutes from '../utils/utils';
function Table(props) {
const styles = {
div: {
width: 'auto',
height: 'auto',
margin: '0 auto',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
},
};
const [table, setTable] = useState();
useEffect(() => {
setTable(<table >
<thead>
<tr>
{props.headerData.map(header => {
console.log("table header = ", header);
{header.map((item, i) => {
console.log("header item", item.name)
return (<th
} key={i}>{item.name}</th>
)})}
})}
</tr>
</thead>
<tbody>
{props.data.map((array, index) => (
<tr key={index}>
{array.map((item, i) => {
return (
<tr>
<td key={i}>{item.nameValue} </td>
</tr>
)
})}
</tr>
))}
</tbody>
</table>)
}, [props.data, props.header]);
return (
<div style={styles.div}>
{table}
</div>
)
}
export default Table;
You're not returning anything from the outer map().
Change the thead to:
<thead>
<tr>
{
props.headerData.map(header => {
console.log("table header = ", header);
return header.map((item, i) => {
console.log("header item", item.name)
return (<th key={i}>{item.name}</th>)
})
}}
}
</tr>
</thead>
Note the return before header.map
That said, you shouldn't save the DOM element to the useState. Rather save the data to the state, and then in your return render function render the table
I am running into an issue with trying to render a table in react using react table. I've narrowed the error down to the data variable around line 13 in figure 1.
When I hard code the data, as shown in figure 1, I have no issues and everything works fine. This hard coded array is a direct copy of the baselineData prop being passed into the PrectionsTable component.
My error only happens when the data object is changed to as shown in figure 2. Instead of the data being hard coded, I am spreading the baselineData prop data.
Figure 3 shows baselineData in react web dev tools and is in fact an array of objects located on the PredictionsTable.
The specific error I am making can be seen in figure 4.
Figure 1
import { useTable } from 'react-table';
import React from 'react';
import SpinnerCustom from '../Spinner.js';
export default function PredictionsTable({
predictions,
baselineData,
loading,
}) {
const checkIfValueIsNumberAndRound = (x) =>
typeof x === 'number' ? x.toFixed(2) : 'n/a';
const data = React.useMemo(() => {
return [
{
make_cut: 0.837075,
player_name: 'Finau, Tony',
top_10: 0.42559600288600297,
top_20: 0.5817631080031079,
top_5: 0.293940416666667,
win: 0.105025,
},
{
make_cut: 0.71215,
player_name: 'Day, Jason',
top_10: 0.228348582528583,
top_20: 0.366261590839716,
top_5: 0.136055932539683,
win: 0.0341,
},
{
make_cut: 0.719925,
player_name: 'Harman, Brian',
top_10: 0.223825056748807,
top_20: 0.366779750145688,
top_5: 0.12981958333333302,
win: 0.032675,
},
{
make_cut: 0.710225,
player_name: 'Montgomery, Taylor',
top_10: 0.21918973443223397,
top_20: 0.352992788086097,
top_5: 0.130785406746032,
win: 0.0324,
},
];
}, [baselineData]);
const columns = React.useMemo(
() => [
{
Header: 'Player',
accessor: 'player_name', // accessor is the "key" in the data
},
{
Header: 'Make Cut',
accessor: 'make_cut',
Cell: (props) =>
checkIfValueIsNumberAndRound(props.cell.row.original.make_cut),
},
{
Header: 'Top 20',
accessor: 'top_20',
Cell: (props) =>
checkIfValueIsNumberAndRound(props.cell.row.original.top_20),
},
{
Header: 'Top 10',
accessor: 'top_10',
Cell: (props) =>
checkIfValueIsNumberAndRound(props.cell.row.original.top_20),
},
{
Header: 'Top 5',
accessor: 'top_5',
Cell: (props) =>
checkIfValueIsNumberAndRound(props.cell.row.original.top_20),
},
{
Header: 'Win',
accessor: 'win',
Cell: (props) =>
checkIfValueIsNumberAndRound(props.cell.row.original.top_20),
},
],
[]
);
const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } =
useTable({ columns, data });
return loading ? (
<SpinnerCustom />
) : (
<table {...getTableProps()} style={{ border: 'solid 1px blue' }}>
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th
{...column.getHeaderProps()}
style={{
borderBottom: 'solid 3px red',
background: 'aliceblue',
color: 'black',
fontWeight: 'bold',
}}
>
{column.render('Header')}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map((row, i) => {
prepareRow(row);
return (
<tr key={i} {...row.getRowProps()}>
{row.cells.map((cell, y) => {
return (
<td
{...cell.getCellProps()}
key={y}
style={{
padding: '10px',
border: 'solid 1px gray',
background: 'papayawhip',
}}
>
{cell.render('Cell')}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
);
}
Figure 2
const data = React.useMemo(() => {
return [...baselineData];
}, [baselineData]);
Figure 3
Figure 4 (ERROR)
this problem may related to initial data for baseLineData.
change the usesState initialize:
const [baselineData, setBaselineData] = useState([]);
I am writing the react-table getting started with typescript, however I am getting an error on the data I am passing into my table function:
utils.js:15 Uncaught TypeError: columns.map is not a function
at linkColumnStructure (utils.js:15:1)
at useTable.js:150:1
at mountMemo (react-dom.development.js:17057:1)
at Object.useMemo (react-dom.development.js:17517:1)
at Object.useMemo (react.development.js:1645:1)
at useTable (useTable.js:149:1)
at Table (App.tsx:9:1)
at renderWithHooks (react-dom.development.js:16141:1)
at mountIndeterminateComponent (react-dom.development.js:20838:1)
at beginWork (react-dom.development.js:22342:1)
Being new to react/typescript pointers much appreciated on my App.tsx below, if I comment out the Table function the page loads, so I am doing something wrong with my types just not sure what.
import React from 'react';
import { useTable } from 'react-table';
import logo from './logo-main.svg';
import './App.css';
function Table(columns: any, data: any) : any
{
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow
} = useTable({ columns, data });
// Render the UI for your table
return (
<table {...getTableProps()} style={{ border: 'solid 1px blue' }}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th
{...column.getHeaderProps()}
style={{
borderBottom: 'solid 3px red',
background: 'aliceblue',
color: 'black',
fontWeight: 'bold',
}}
>
{column.render('Header')}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map(row => {
prepareRow(row)
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return (
<td
{...cell.getCellProps()}
style={{
padding: '10px',
border: 'solid 1px gray',
background: 'papayawhip',
}}
>
{cell.render('Cell')}
</td>
)
})}
</tr>
)
})}
</tbody>
</table>
)
}
function App() {
const data = React.useMemo(
() => [
{
col1: 'Hello',
col2: 'World',
},
{
col1: 'react-table',
col2: 'rocks',
},
{
col1: 'whatever',
col2: 'you want',
},
],
[]
)
const columns = React.useMemo(
() => [
{
Header: 'Column 1',
accessor: 'col1', // accessor is the "key" in the data
},
{
Header: 'Column 2',
accessor: 'col2',
},
],
[]
)
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>Edit <code>src/App.tsx</code> and save to reload!!!</p>
<a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer">Learn React today </a>
<Table columns={columns} data={data} />
</header>
</div>
);
}
export default App;
You have
<Table columns={columns} data={data} />
so inside Table the columns and data are part of the first argument, the props object.
function Table({ columns, data }: any) : any
But you should really type your identifiers properly - using any completely defeats the purpose of using TypeScript. Proper typing will let you catch these sorts of problems earlier, during compilation.
type Column = { Header: string, accessor: string };
type Data = { col1: string, col2: string };
function Table({
columns,
data,
}: {
columns: Array<Column>,
data: Array<Data>
}) {
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow
} = useTable({ columns, data });
// etc
(You don't need to note the return type when TypeScript can infer it automatically - omitting an explicit type when TS can infer it is much preferable to setting it to any)
Your Table function should be
function Table({columns: any, data: any}) : any
instead of
function Table(columns: any, data: any) : any
Because you are passing columns and data as props from here
<Table columns={columns} data={data} />
when call the retriveStudentPhoto() function from h1 tag . I can't get the value return .How do I solve this.
Thanks in advance .
{data.map((val, index) => (
<tr>
<td style={{ outline: 'none' }} data-heading="Photo">
<h1{ retriveStudentPhoto(val.email) }</h1> /*call
this function */
</td>
))}
function retriveStudentPhoto (email) {
var photo = ""
axios.get(`/user-info/${email}`)
.then((result)=>{
photo = result.data[0].profile_photo;
}).catch((error)=>error)
return photo ;
}
Since the function is inside the map , there will be call multiple times and the value will be returned each time. I used the useState hook but not getting results as per demand.
please help me more. Thanks in advance.
complete Code :
import React, { useEffect, useState } from 'react';
import { Button, Alert } from 'react-bootstrap';
import $ from 'jquery';
import axios from '../../config/axios';
import '../Attendance_Table/css/App.scss';
import { useAuth } from "../../contexts/AuthContext";
import { Link, useHistory } from 'react-router-dom';
function PeopleList({ match }) {
const course_code = match.params.course_code;
const { currentUser } = useAuth();
const history = useHistory();
const [msg, setMsg] = useState("");
const [course_name, setcourse_name] = useState("");
var breakOn = 'medium'
let tableClass = 'table-container__table';
if (breakOn === 'small') {
tableClass += ' table-container__table--break-sm';
} else if (breakOn === 'medium') {
tableClass += ' table-container__table--break-md';
} else if (breakOn === 'large') {
tableClass += ' table-container__table--break-lg';
}
const headingColumns = ['Photo', 'Student ID', 'Name', 'Email']
const headingColumns2 = ['Photo', 'Name', 'Email']
const [data, setData] = useState([]);
const [studentsPhoto, setStudentsPhoto] = useState([]);
const [techInfo, setTechInfo] = useState([]);
useEffect(() => {
axios.get(`/course/info/${course_code}`).then((result) => {
setcourse_name(result.data[0].course_name)
})
SearchHandle();
}, [])
let SearchHandle = () => {
axios.get(`people/${course_code}`).then((result) => {
setData(result.data)
// teacher info retrieve
axios.get(`user-info/${result.data[0].tecEmail}`)
.then((res) => {
setTechInfo(res.data[0]);
})
//close teacher retrieve
let student_photo = [];
let promises = [];
for (let i = 0; i < result.data.length; i++) {
promises.push(
axios.get(`/user-info/${result.data[i].email}`)
.then((res) => {
student_photo.push(res?.data[0]?.profile_photo)
})
)
}
Promise.all(promises).then(() => {
setStudentsPhoto(student_photo);
})
}).catch((err) => console.log(err))
}
function retriveStudentPhoto(email) {
var photo = "";
axios.get(`/user-info/${email}`)
.then((result) => {
var photo = result.data[0].profile_photo;
}).catch((error) => error)
return photo;
}
return (
<div>
{/* {msg ? ( */}
<div>
<div className="table-container" style={{ backgroundColor: 'white' }}>
<div className="table-container__title">
<h5>People -- {course_name}({course_code})</h5>
</div>
<h5 style={{ padding: '10px', fontFamily: 'roboto' }}>Teacher</h5>
<table style={{ outline: 'none', border: 'none' }} className={tableClass}>
<thead>
<tr>
{headingColumns2.map((col, index) => (
<th data-heading={index} key={index}>{col}</th>
))}
</tr>
</thead>
<tbody>
<tr>
<td style={{ outline: 'none' }} data-heading="Photo">
{techInfo.profile_photo ? (
<img style={{ borderRadius: '150px', height: '40px', width: '40px' }}
src={techInfo.profile_photo} />
) : (
<img style={{ borderRadius: '150px', height: '40px', width: '40px' }}
src="https://lh3.googleusercontent.com/-XdUIqdMkCWA/AAAAAAAAAAI/AAAAAAAAAAA/4252rscbv5M/s75-c-fbw=1/photo.jpg" />
)}
</td>
<td data-heading="Student Name">{techInfo.name} </td>
<td data-heading="Student Email"><span style={{ fontSize: '11.5px' }}>{techInfo.email}</span> </td>
</tr>
</tbody>
</table><br />
<br />
{data.length ? (
<table style={{ outline: 'none', border: 'none' }} className={tableClass}>
<thead>
<tr>
{headingColumns.map((col, index) => (
<th data-heading={index} key={index}>{col}</th>
))}
</tr>
</thead>
<tbody>
{data.map((val, index) => (
<tr>
<td style={{ outline: 'none' }} data-heading="Photo">
<h1>{retriveStudentPhoto(val.email)}</h1>
</td>
<td style={{ outline: 'none' }} data-heading="Student ID">{val.student_id}</td>
<td data-heading="Student Name">{val.student_name} </td>
<td data-heading="Student Email"><span style={{ fontSize: '11.5px' }}>{val.email}</span> </td>
</tr>
))}
</tbody>
</table>
) : (
<div>
<Alert className="md-4 w-100" variant="danger">
No Student add to this course yet
</Alert>
</div>
)}
<br />
<div style={{ textAlign: 'center', paddingBottom: '5px' }}>
<Link to={`/attendance-sheet/${course_code}`}><span>Back to {course_code}</span></Link>
</div>
</div>
</div>
{/* ) : ""} */}
</div>
);
}
export default PeopleList;
Render is a pure, synchronous function. If you need to make asynchronous calls then do this in a lifecycle, in this case, a useEffect hook and store the results in state to be rendered.
Iterate over the result.data array and issue the GET request, and when the request resolves issue a functional state update to create a new data array, using the iterated element's index to inject a new photo property.
const SearchHandle = () => {
axios.get(`people/${course_code}`)
.then((result) => {
setData(result.data);
result.data.forEach(async (item, index) => {
try {
const result = await axios.get(`/user-info/${item.email}`);
const photo = result.data[0].profile_photo;
setData(data => data.map(
(el, i) => i === index
? { ...el, photo }
: el)
)
} catch(error) {
// log error, etc...
}
});
...
}
In the render access the new photo property you added above.
{data.map((val, index) => (
<tr key={val.student_name}>
<td style={{ outline: 'none' }} data-heading="Photo">
<h1>{val.photo}</h1>
</td>
<td style={{ outline: 'none' }} data-heading="Student ID">{val.student_id}</td>
<td data-heading="Student Name">{val.student_name} </td>
<td data-heading="Student Email">
<span style={{ fontSize: '11.5px' }}>{val.email}</span>
</td>
</tr>
)
)}
You are trying to use a function with async data in the render, so probably you dont have the data yet.
Try to keep the data from that axios.get in the state, youd probably want also to implement useEffect to make the call and store it to the state when component mounts
I have a react table that looks like this.
I want to be able to click each individual cell and get the value. I know you can get the entire row, but I only want each individual cell when it's clicked so I can save it in state like this
{
"Team": GB,
"MoneyLine": -120
}
{
"Team": GB,
"Spread": 1
}
{
"Team": GB,
"MoneyLine": -120,
"Spread": -1,
"Total": 48
}
I tried this link How to get cell value on React-Table?
but it doesn't provide much explanation.
I need to get the team name whenever any cell in the row is clicked and then the specific value that is clicked.
Here is the code
import React, { useState } from 'react'
import styled from 'styled-components'
import { useTable } from 'react-table'
import data from '../Data/data.json'
const Styles = styled.div`
padding: 1rem;
table {
border-spacing: 0;
border: 1px solid black;
tr {
:last-child {
td {
border-bottom: 0;
}
}
}
th,
td {
margin: 0;
padding: 0.5rem;
border-bottom: 1px solid black;
border-right: 1px solid black;
:last-child {
border-right: 0;
}
}
}
`
function Table({ columns, data }) {
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
} = useTable({
columns,
data,
})
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.map((row, _) => {
prepareRow(row)
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return <td {...cell.getCellProps()} >{cell.render('Cell')}</td>
})}
</tr>
)
})}
</tbody>
</table>
)
}
export default function PicksTable(){
const [team, setTeam] = useState('');
const [moneyLine, setMoneyLine] = useState('');
const [spread, setSpread] = useState('');
const [total, setTotal] = useState('');
const columns = React.useMemo(
() => [
{
Header: 'Teams',
columns: [
{
Header: 'Team',
accessor: 'team',
},
],
},
{
Header: 'Betting Info',
columns: [
{
Header: 'Money Line',
accessor: 'moneyLine',
},
{
Header: 'Spread',
accessor: 'spread',
},
{
Header: 'Total',
accessor: 'total',
},
],
},
],
[]
)
return (
<Styles>
<Table columns={columns} data={data} />
</Styles>
)
}
You need to add onClick to <td> (values are in row and cell):
<td
onClick={() => console.info(row.values.team, cell.value)}
{...cell.getCellProps()}
>
Here is a full working CodeSandbox:
https://codesandbox.io/s/fervent-shape-jr8tm?file=/src/App.js:1325-1489