Get cell value from React table - reactjs

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

Related

HeIp solve Error in React. (Uncaught TypeError: baselineData is not iterable). baselineData is clearly an array. PLEASE heIp

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([]);

How to make react table to scrollable?

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",

How can i display data on my react table each second by adding new row per second using react and react hooks?

I am trying to get json data each second to add as a new row in my react table.
In the console it seems data is coming up each second but in the UI none of the data is popping up. Couldn't figure out why any of that table row in not being displayed. The snippet below does not work in stackoverflow please use the sandbox for running the code.
import React, { useState, useEffect } from "react";
import { useTable } from "react-table";
// import "./styles.css";
const API_DATA_RETURNED = [
{
// I tried id: "1" as well
id: "1",
name: "TEMP001",
serialNum: "Temp Sensor",
status: "Active",
},
{
id: "2",
name: "TEMP002",
serialNum: "Temp Sensor",
status: "Unknown",
},
{
id: "3",
name: "HUM003",
serialNum: "Humidity Sensor",
status: "Active",
},
{
id: "4",
name: "HUM004",
serialNum: "Humidity Sensor",
status: "Active",
},
{
id: "5",
name: "HUM005",
serialNum: "Humidity Sensor",
status: "Active",
},
];
function SensorTable({ columns, data }) {
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 SensorContainer() {
const [page, setPage] = useState(0);
const [sensors, setSensors] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
function loadData(index) {
console.log("loadData called");
let newData = sensors;
console.log(newData);
newData.push(API_DATA_RETURNED[index]);
setSensors(newData);
console.log(sensors);
setLoading(false);
}
setLoading(true);
// GET sensor list from API
const timer = window.setInterval(() => {
if (page < API_DATA_RETURNED.length) {
loadData(page);
setPage(page + 1);
}
}, 1000);
return () => window.clearInterval(timer);
}, [page, sensors]); // This is self is componentDidMount
const columns = React.useMemo(
() => [
{
Header: "ID",
accessor: "id", // accessor is the "key" in the data
},
{
Header: "Name",
accessor: "name",
},
{
Header: "Serial Number",
accessor: "serialNum",
},
{
Header: "Status",
accessor: "status",
},
],
[]
);
if (sensors.length === 0 && !loading) {
return <div>No Senors data available</div>;
}
return (
<div>
{loading && <span>Please wait we are fetching data</span>}
<SensorTable columns={columns} data={sensors} />
</div>
);
}
export default function App() {
return <SensorContainer />;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Same CODE in sandbox

Remove unwanted characters from a string in table cell

I have a React table (react-table) that takes in data from an api to populate the row cells. The data looks something like this:
const data = [{id: "6666", contact_id: "1", poc_id: "2", contact: "John Doe",
watersheds: "{12070104,12090301,12090401}"}, {id: "6667", contact_id: "2",
poc_id: "3", contact: "Sally Jones", watersheds: "{12070105,12090301}"}]
There could be more or less items depending on what the API request returns. My issue is that when the Watershed column is populated with the watershed data item, it includes the { ... }s. I want it to return 12070104,12090301,12090401 in the cell, not {12070104,12090301,12090401}. I know I can use replace or some other method to remove and replace the unwanted { }s:
data.map(item => (item.watersheds.replace(/{/gi, "").replace(/}/gi, "")))
or something similar, but since I adding the data to the table dynamically, I'm not sure how to remove the { }s before they are displayed in the table? My table looks something like this:
import React from 'react'
import styled from 'styled-components'
import { useTable } from 'react-table'
import makeData from './makeData'
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, i) => {
prepareRow(row)
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
})}
</tr>
)
})}
</tbody>
</table>
)
}
function App() {
const columns = React.useMemo(
() => [
{
Header: 'ID',
accessor: 'id',
},
{
Header: 'CONTACT ID',
accessor: 'contact_id',
},
{
Header: 'POC ID',
accessor: 'age',
},
{
Header: 'NAME',
accessor: 'contact',
},
{
Header: 'Watersheds',
accessor: 'watersheds',
}
], []
)
//const data = React.useMemo(() => makeData(20), [])
const data = useMemo(() => [{id: "6666", contact_id: "1", poc_id: "2", contact: "John Doe",
watersheds: "{12070104,12090301,12090401}"}, {id: "6667", contact_id: "2",
poc_id: "3", contact: "Sally Jones", watersheds: "{12070105,12090301}"}])
return (
<Styles>
<Table columns={columns} data={data} />
</Styles>
)
}
export default App
You can render a custom content for each cell . Instead of transforming the data before passing to the table . we can just transform the value before rendering .
{
Header: "Watersheds",
accessor: "watersheds",
Cell: (props) => {
return <p>{props.value.replace(/{/gi, "").replace(/}/gi, "")}</p>;
}
}
Working Sandbox
In your App component, right before using useMemo, you can extract that array of objects to a separate varible, iterate over watersheds and replace them with the regex that you used above. After that you can pass it to useMemo.

Dynamically add a column to react table on button click

I am trying to add a column to the react table using an add column button, but I am a little confused on how to implement this. I want to be able to add a column to the corresponding side that the button is clicked. Do I need to create a custom function and add it to my columns array or is there an easier way to implement this?
Here is the code.
/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable jsx-a11y/no-static-element-interactions */
/* eslint-disable guard-for-in */
/* eslint-disable no-restricted-syntax */
/* eslint-disable react/jsx-props-no-spreading */
import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
import { useTable } from 'react-table';
import data from '../Data/data.json';
import './Table.css';
const Styles = styled.div`
table {
border-spacing: 0;
border: 1px solid black;
width: 970px;
tr {
:last-child {
td {
border-bottom: 0;
height: 500px;
}
}
}
th,
td {
margin: 0;
padding: 0.5rem;
border-bottom: 1px solid black;
border-right: 1px solid black;
:last-child {
border-right: 0;
}
}
}
`;
const HarveyBall = (initialValue) => {
const [value, setValue] = useState(initialValue);
const onClick = () => {
if(value === 'Empty'){
setValue('Quarter');
}
if(value === 'Quarter'){
setValue('Half');
}
if(value === 'Half'){
setValue('Three-Quarter');
}
if(value === 'Three-Quarter'){
setValue('Full');
}
if(value === 'Full'){
setValue('Empty');
}
};
if(value === "Empty"){
return (
<div type="button" label="Empty" className="harvey none" onClick={onClick} />
);
}
if(value === "Quarter"){
return (
<div type="button" label="Quarter" className="harvey quarters quarter" onClick={onClick} />
);
}
if(value === "Half"){
return (
<div type="button" label="Half" className="harvey quarters half" onClick={onClick} />
);
}
if(value === "Three-Quarter"){
return (
<div type="button" label="Three-Quarter" className="harvey quarters three-quarters" onClick={onClick} />
);
}
if(value === "Full"){
return (
<div type="button" label="Full" className="harvey quarters full" onClick={onClick} />
);
}
return null;
};
const defaultPropGetter = () => ({});
const EditableCell = ({
value: initialValue,
row: { index },
column: { id },
updateMyData,
}) => {
const [value, setValue] = React.useState(initialValue);
const onChange = e => {
setValue(e.target.value);
};
const onBlur = () => {
updateMyData(index, id, value);
};
React.useEffect(() => {
setValue(initialValue);
}, [initialValue]);
return id === "strategicInitiative" || index === 5 ? <textarea value={value} onChange={onChange} onBlur={onBlur} style={{ width: '100%', focus: 'none', outline: 'none', border: 'none',resize: 'none', expand: {height: '1em', width: '50%', padding: '3px'}}}/> : HarveyBall(initialValue);
};
const defaultColumn = {
Cell: EditableCell,
};
function Table({ columns, getHeaderProps = defaultPropGetter, updateMyData, }) {
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
} = useTable({
columns,
data,
updateMyData,
defaultColumn,
});
return (
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => column.hideHeader === false ? null : (
<th
{...column.getHeaderProps([
{
className: column.className,
style: column.style,
},
getHeaderProps(column),
])}
>
{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 MatrixTable() {
const addTasks = () => {
const taskLength = [];
for(let i = 0; i < data.length; i += 1){
for(const [key] of Object.entries(data[i])){
if(key.includes("T")){
taskLength.push(key[1]);
}
}
}
const newTaskLength = (parseInt(taskLength[taskLength.length - 1], 10) + 1) ;
for(let i = 0; i < data.length; i += 1){
data[i][`T${newTaskLength}`] = "";
}
};
const taskButton = () => (
<>
<div>Tasks</div>
<button onClick={addColumns} type='button'>Add Column</button>
</>
);
const goalButton = () => (
<>
<div>Goals</div>
<button type='button'>Add Column</button>
</>
);
const columns = React.useMemo(
() => [
{
Header: () => taskButton(),
accessor: 'tasks',
style: {
width: '255px',
height: '49px',
background: '#fdf5ed',
fontSize: '16px',
color: '#f2994a',
textAlign: 'center',
lineHeight: '49px',
},
columns: [
{
hideHeader: false,
accessor: 'T1'
},
{
hideHeader: false,
accessor: 'T2'
},
{
hideHeader: false,
accessor: 'T3'
},
{
hideHeader: false,
accessor: 'T4'
},
{
hideHeader: false,
accessor: 'T5'
},
]
},
{
Header: "Strategic Inititiatives",
accessor: "strategicInitiative ",
style: {
color: '#323b3e',
width: '460px',
height: '49px',
background: '#f2f2f2',
textAlign: 'center',
lineHeight: '49px',
},
columns: [
{
hideHeader: false,
accessor: 'strategicInitiative'
}
]
},
{
Header: goalButton(),
accessor: 'goals',
style: {
color: '#56ccf2',
width: '255px',
height: '49px',
background: '#f8fcfe',
textAlign: 'center',
lineHeight: '49px',
},
columns: [
{
hideHeader: false,
accessor: 'G1'
},
{
hideHeader: false,
accessor: 'G2'
},
{
hideHeader: false,
accessor: 'G3'
},
{
hideHeader: false,
accessor: 'G4'
},
{
hideHeader: false,
accessor: 'G5'
}
]
},
],
[]
);
const addColumns = () => {
for(let i = 0; i < columns.length; i += 1) {
if(columns[i].accessor === "tasks"){
console.log(columns[i]);
columns[i].columns.push({
hideHeader: false,
accessor: 'T6'
});
}
}
};
addColumns();
const [, setData] = useState(data);
useEffect(() => {
setData(data);
}, [data]);
const updateMyData = (rowIndex, columnId, value) => {
setData(old =>
old.map((row, index) => {
if (index === rowIndex) {
return {
...old[rowIndex],
[columnId]: value,
};
}
return row;
})
);
};
return (
<Styles>
<Table columns={columns} data={data} updateMyData={updateMyData}/>
</Styles>
);
}
I would imagine that modifying the array columns which stores the data which the table uses to render, would be the correct solution. Once columns is updated, your table should re-render which will then include the updated column.
Yeah, changing columns array(not by mutation, but by setting columns prop to new required array) is the best way to go.

Resources