Related
I am trying to create a react data table and filter the data in the table through mui textfield in a mui Modal.
Here is the image
The filters in every column must open in the mui modal .Like this example Like this example They must have the functionality to search.
Kindly suggest me the code to filter every column separately through mui text fields in the same Modal
import React, { useState } from 'react'
import styled from 'styled-components'
import { useTable, usePagination, useFilters, useGlobalFilter, useSortBy, useAsyncDebounce } from 'react-table'
import makeData from './makeData'
import MDBTypography from 'components/MDBTypography'
import { Divider, Grid, InputAdornment, Modal, Tooltip } from '#mui/material'
import ArrowDropUpOutlinedIcon from '#mui/icons-material/ArrowDropUpOutlined';
import ArrowDropDownOutlinedIcon from '#mui/icons-material/ArrowDropDownOutlined';
import SearchIcon from "assets/images/icons/svg/medium/SearchIcon.svg";
import { matchSorter } from 'match-sorter'
import MDBInput from 'components/MDBInput'
import MDBButton from 'components/MDBButton'
import FilterListOutlinedIcon from '#mui/icons-material/FilterListOutlined';
import MDBCard from 'components/MDBCard'
const Styles = styled.div`
padding: 1rem;
table {
border-spacing: 0;
border: 1px solid #44474A;
tr {
:last-child {
td {
border-bottom: 0;
}
}
}
th{
background:#1C1F21;
}
th,
td {
margin: 0;
padding: 0.5rem;
border-bottom: 1px solid #44474A;
border-right: 1px solid #44474A;
:last-child {
border-right: 0;
}
}
}
.pagination {
padding: 0.5rem;
}
`
// Define a default UI for filtering
function GlobalFilter({
preGlobalFilteredRows,
globalFilter,
setGlobalFilter,
}) {
const count = preGlobalFilteredRows.length
const [value, setValue] = useState(globalFilter)
const onChange = useAsyncDebounce(value => {
setGlobalFilter(value || undefined)
}, 200)
// Set Filter popover
const [mediaKits, setMediaKits] = useState([]);
const handleClose = () => {
setAnchorEl(null);
}
const [anchorEl, setAnchorEl] = React.useState(null);
const open = Boolean(anchorEl);
const id = open ? 'simple-popover' : undefined;
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
return (
<>
<MDBInput
search={true}
value={value || ""}
fullWidth
InputProps={{
endAdornment: (
<InputAdornment position="end">
<MDBTypography component="img" src={SearchIcon} />
</InputAdornment>
),
}}
onChange={e => {
setValue(e.target.value);
onChange(e.target.value);
}}
placeholder={`Search ${count} records...`}
variant="standard"
width="400px"
/>
<MDBButton
size="medium"
variant="contained"
color="white"
bgColor="smoke_light"
fontWeight="medium"
fontSize="md"
borderSize="md"
lineHeight="2xxl"
aria-describedby={id}
onClick={handleClick}
>
<FilterListOutlinedIcon
color="white"
/>
</MDBButton>
<Modal
id={id}
open={open}
onClose={handleClose}
>
<>
<MDBCard bgcolor="black" >
{/* //sx={{ px: 0, pt:0, pb: 1.5, width:"min-content"}} */}
<Grid sx={{ px: 2.5, py: 2 }}>
<MDBTypography sx={{ pb: 1 }}>
Filters
</MDBTypography>
</Grid>
<Divider />
<Tooltip title={`Search ${count} records...`}>
<MDBInput
sx={{ pr: 4, input: { cursor: 'pointer' } }}
placeholder="Filter First Name..."
/>
</Tooltip>
<Divider />
<Tooltip title={`Search ${count} records...`}>
<MDBInput
sx={{ pr: 4, input: { cursor: 'pointer' } }}
placeholder="Filter Last Name..."
/>
</Tooltip>
<Divider />
<Tooltip title={`Search ${count} records...`}>
<filterGreaterThan />
</Tooltip>
</MDBCard>
</>
</Modal>
</>
)
}
// Define a default UI for filtering
function DefaultColumnFilter({
column: { filterValue, preFilteredRows, setFilter },
}) {
const count = preFilteredRows.length
return (
<input
value={filterValue || ''}
onChange={e => {
setFilter(e.target.value || undefined) // Set undefined to remove the filter entirely
}}
placeholder={`Search ${count} records...`}
/>
)
}
// This is a custom filter UI for selecting
// a unique option from a list
function SelectColumnFilter({
column: { filterValue, setFilter, preFilteredRows, id },
}) {
// Calculate the options for filtering
// using the preFilteredRows
const options = React.useMemo(() => {
const options = new Set()
preFilteredRows.forEach(row => {
options.add(row.values[id])
})
return [...options.values()]
}, [id, preFilteredRows])
// Render a multi-select box
return (
<select
value={filterValue}
onChange={e => {
setFilter(e.target.value || undefined)
}}
>
<option value="">All</option>
{options.map((option, i) => (
<option key={i} value={option}>
{option}
</option>
))}
</select>
)
}
// This is a custom filter UI that uses a
// slider to set the filter value between a column's
// min and max values
function SliderColumnFilter({
column: { filterValue, setFilter, preFilteredRows, id },
}) {
// Calculate the min and max
// using the preFilteredRows
const [min, max] = React.useMemo(() => {
let min = preFilteredRows.length ? preFilteredRows[0].values[id] : 0
let max = preFilteredRows.length ? preFilteredRows[0].values[id] : 0
preFilteredRows.forEach(row => {
min = Math.min(row.values[id], min)
max = Math.max(row.values[id], max)
})
return [min, max]
}, [id, preFilteredRows])
return (
<>
<input
type="range"
min={min}
max={max}
value={filterValue || min}
onChange={e => {
setFilter(parseInt(e.target.value, 10))
}}
/>
<button onClick={() => setFilter(undefined)}>Off</button>
</>
)
}
// This is a custom UI for our 'between' or number range
// filter. It uses two number boxes and filters rows to
// ones that have values between the two
function NumberRangeColumnFilter({
column: { filterValue = [], preFilteredRows, setFilter, id },
}) {
const [min, max] = React.useMemo(() => {
let min = preFilteredRows.length ? preFilteredRows[0].values[id] : 0
let max = preFilteredRows.length ? preFilteredRows[0].values[id] : 0
preFilteredRows.forEach(row => {
min = Math.min(row.values[id], min)
max = Math.max(row.values[id], max)
})
return [min, max]
}, [id, preFilteredRows])
return (
<div
style={{
display: 'flex',
}}
>
<input
value={filterValue[0] || ''}
type="number"
onChange={e => {
const val = e.target.value
setFilter((old = []) => [val ? parseInt(val, 10) : undefined, old[1]])
}}
placeholder={`Min (${min})`}
style={{
width: '70px',
marginRight: '0.5rem',
}}
/>
to
<input
value={filterValue[1] || ''}
type="number"
onChange={e => {
const val = e.target.value
setFilter((old = []) => [old[0], val ? parseInt(val, 10) : undefined])
}}
placeholder={`Max (${max})`}
style={{
width: '70px',
marginLeft: '0.5rem',
}}
/>
</div>
)
}
function fuzzyTextFilterFn(rows, id, filterValue) {
return matchSorter(rows, filterValue, { keys: [row => row.values[id]] })
}
// Let the table remove the filter if the string is empty
fuzzyTextFilterFn.autoRemove = val => !val
function Table({ columns, data }) {
const filterTypes = React.useMemo(
() => ({
// Add a new fuzzyTextFilterFn filter type.
fuzzyText: fuzzyTextFilterFn,
// Or, override the default text filter to use
// "startWith"
text: (rows, id, filterValue) => {
return rows.filter(row => {
const rowValue = row.values[id]
return rowValue !== undefined
? String(rowValue)
.toLowerCase()
.startsWith(String(filterValue).toLowerCase())
: true
})
},
}),
[]
)
const defaultColumn = React.useMemo(
() => ({
// Let's set up our default Filter UI
Filter: DefaultColumnFilter,
}),
[]
)
// Use the state and functions returned from useTable to build your UI
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
state,
visibleColumns,
preGlobalFilteredRows,
setGlobalFilter,
page, // Instead of using 'rows', we'll use page,
// which has only the rows for the active page
// The rest of these things are super handy, too ;)
canPreviousPage,
canNextPage,
pageOptions,
pageCount,
gotoPage,
nextPage,
previousPage,
setPageSize,
state: { pageIndex, pageSize },
} = useTable(
{
columns,
data,
defaultColumn, // Be sure to pass the defaultColumn option
filterTypes,
initialState: { pageIndex: 0 },
},
useFilters, // useFilters!
useGlobalFilter,
useSortBy,
usePagination,
)
// Render the UI for your table
const firstPageRows = rows.slice(0, 20)
return (
<>
{/* <pre>
<code>
{JSON.stringify(
{
pageIndex,
pageSize,
pageCount,
canNextPage,
canPreviousPage,
},
null,
2
)}
</code>
</pre> */}
<GlobalFilter
preGlobalFilteredRows={preGlobalFilteredRows}
globalFilter={state.globalFilter}
setGlobalFilter={setGlobalFilter}
/>
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()} >
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps(column.getSortByToggleProps())}>
{column.render('Header')}
{/* Add a sort direction indicator */}
<Grid
container
direction="row"
justifyContent='flex-end'
alignItems='flex-end'
>
<Grid
container
direction='row'
justifyContent="flex-end"
alignItems="flex-end"
sx={{ mt: -4.9, ml: 4 }}
>
{
column.isSorted ?
column.isSortedDesc
? <ArrowDropUpOutlinedIcon color='white' sx={{ mb: 1 }} />
: <ArrowDropDownOutlinedIcon color='white' />
: ''
}
</Grid>
</Grid>
<div>{column.canFilter ? column.render('Filter') : null}</div>
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{page.map((row, i) => {
prepareRow(row)
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return (<td {...cell.getCellProps()}>{cell.render('Cell')}</td>)
})}
</tr>
)
})}
</tbody>
</table>
{/*
Pagination can be built however you'd like.
This is just a very basic UI implementation:
*/}
<Grid
container
className="pagination"
sx={{ ml: 42 }}
>
<Grid>
<MDBTypography fontWeight="regular" fontSize="xxs" lineHeight='md' sx={{ mt: 1 }}>
Page{' '}
{/* <strong> */}
{pageIndex + 1} of {pageOptions.length} |
{/* </strong>{' '} */}
</MDBTypography>
</Grid>
<Grid sx={{ ml: 1, mt: -0.4 }}>
<button onClick={() => gotoPage(0)} disabled={!canPreviousPage}>
{'<<'}
</button>{' '}
<button onClick={() => previousPage()} disabled={!canPreviousPage}>
{'<'}
</button>{' '}
<button onClick={() => nextPage()} disabled={!canNextPage}>
{'>'}
</button>{' '}
<button onClick={() => gotoPage(pageCount - 1)} disabled={!canNextPage}>
{'>>'}
</button>{' '}
</Grid>
{/* <span>
| Go to page:{' '}
<input
type="number"
defaultValue={pageIndex + 1}
onChange={e => {
const page = e.target.value ? Number(e.target.value) - 1 : 0
gotoPage(page)
}}
style={{ width: '100px' }}
/>
</span>{' '}
<select
value={pageSize}
onChange={e => {
setPageSize(Number(e.target.value))
}}
>
{[10, 20, 30, 40, 50].map(pageSize => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</select> */}
</Grid>
</>
)
}
function filterGreaterThan(rows, id, filterValue) {
return rows.filter(row => {
const rowValue = row.values[id]
return rowValue >= filterValue
})
}
function App() {
const [isOpen, setIsOpen] = useState(false);
const handleOpen = () => {
console.log('This is a test')
setIsOpen(true);
}
const handleClose = () => setIsOpen(false);
const columns = React.useMemo(
() => [
{
Header: <MDBTypography sx={{ mr: 2 }}>First Name</MDBTypography>,
accessor: 'firstName',
},
{
Header: <MDBTypography sx={{ mr: 2 }}>Last Name</MDBTypography>,
accessor: 'lastName',
filter: 'fuzzyText'
},
{
Header: <MDBTypography sx={{ mr: 2 }}>Age</MDBTypography>,
accessor: 'age',
Filter: SliderColumnFilter,
filter: 'equals'
},
{
Header: <MDBTypography sx={{ mr: 2 }}>Visits</MDBTypography>,
accessor: 'visits',
filter: filterGreaterThan,
Filter: NumberRangeColumnFilter,
filter: 'between',
},
{
Header: <MDBTypography sx={{ mr: 2 }}>Status</MDBTypography>,
accessor: 'status',
Filter: SelectColumnFilter,
filter: 'includes',
},
{
Header: <MDBTypography sx={{ mr: 2 }}>Profile Progress</MDBTypography>,
accessor: 'progress',
Filter: SliderColumnFilter,
filter: filterGreaterThan,
},
{
Header: <MDBTypography sx={{ mr: 2 }}>Edit</MDBTypography>,
accessor: 'edit',
Cell: ({ value }) => (
<Grid
container
justifyContent="space-between">
<MDBButton variant="outlined" onClick={() => handleEdit(value)}>Edit</MDBButton>
<MDBButton variant="outlined" sx={{ backgroundColor: "red" }} onClick={handleOpen}>Delrte</MDBButton>
<Modal
// open={isOpen}
// onClose={handleClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<MDBCard bgcolor="black" >
Are You Sure :
<MDBButton>
Yes
</MDBButton>
<MDBButton onClick={handleClose}>
No
</MDBButton>
</MDBCard>
</Modal>
</Grid>
)
},
],
[]
)
function handleEdit(row) {
console.log(row);
}
function HandleDelete(row) {
console.log(row)
setIsOpen(true)
console.log("Set is open done")
}
const data = React.useMemo(() => makeData(100).map((item) => ({ ...item, edit: item })))
return (
<Styles>
<MDBTypography fontWeight="regular" fontSize="xxs" lineHeight='md'>
<Table columns={columns} data={data} />
</MDBTypography>
</Styles>
)
}
export default App
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'm displaying user data using react-table with select all checkbox. but i want to add one more checkbox column to change active/inactive status. i have added status check box but when i select any status checkbox then 1st column checkbox also get selected.
https://codesandbox.io/s/condescending-lewin-fcukr?file=/src/App.js
import React from "react";
import styled from "styled-components";
import { useTable, usePagination, useRowSelect } 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;
}
}
}
.pagination {
padding: 0.5rem;
}
`;
const IndeterminateCheckbox = React.forwardRef(
({ indeterminate, ...rest }, ref) => {
const defaultRef = React.useRef();
const resolvedRef = ref || defaultRef;
React.useEffect(() => {
resolvedRef.current.indeterminate = indeterminate;
}, [resolvedRef, indeterminate]);
return (
<>
<input type="checkbox" ref={resolvedRef} {...rest} />
</>
);
}
);
const IndeterminateCheckbox2 = React.forwardRef(
({ indeterminate, ...rest }, ref) => {
const defaultRef = React.useRef();
const resolvedRef = ref || defaultRef;
React.useEffect(() => {
resolvedRef.current.indeterminate = indeterminate;
}, [resolvedRef, indeterminate]);
return (
<>
<input type="checkbox" name="status" ref={resolvedRef} {...rest} />
</>
);
}
);
function Table({ columns, data }) {
// Use the state and functions returned from useTable to build your UI
const {
getTableProps,
getTableBodyProps,
headerGroups,
prepareRow,
page, // Instead of using 'rows', we'll use page,
// which has only the rows for the active page
// The rest of these things are super handy, too ;)
canPreviousPage,
canNextPage,
pageOptions,
pageCount,
gotoPage,
nextPage,
previousPage,
setPageSize,
selectedFlatRows,
state: { pageIndex, pageSize, selectedRowIds }
} = useTable(
{
columns,
data
},
usePagination,
useRowSelect,
(hooks) => {
hooks.visibleColumns.push((columns) => [
// Let's make a column for selection
{
id: "selection",
// The header can use the table's getToggleAllRowsSelectedProps method
// to render a checkbox
Header: ({ getToggleAllPageRowsSelectedProps }) => (
<div>
<IndeterminateCheckbox {...getToggleAllPageRowsSelectedProps()} />
</div>
),
// The cell can use the individual row's getToggleRowSelectedProps method
// to the render a checkbox
Cell: ({ row }) => (
<div>
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
</div>
)
},
...columns
]);
}
);
// Render the UI for your table
return (
<>
<pre>
<code>
{JSON.stringify(
{
pageIndex,
pageSize,
pageCount,
canNextPage,
canPreviousPage
},
null,
2
)}
</code>
</pre>
<table {...getTableProps()}>
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th {...column.getHeaderProps()}>{column.render("Header")}</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{page.map((row, i) => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map((cell) => {
return (
<td {...cell.getCellProps()}>{cell.render("Cell")}</td>
);
})}
</tr>
);
})}
</tbody>
</table>
{/*
Pagination can be built however you'd like.
This is just a very basic UI implementation:
*/}
<div className="pagination">
<button onClick={() => gotoPage(0)} disabled={!canPreviousPage}>
{"<<"}
</button>{" "}
<button onClick={() => previousPage()} disabled={!canPreviousPage}>
{"<"}
</button>{" "}
<button onClick={() => nextPage()} disabled={!canNextPage}>
{">"}
</button>{" "}
<button onClick={() => gotoPage(pageCount - 1)} disabled={!canNextPage}>
{">>"}
</button>{" "}
<span>
Page{" "}
<strong>
{pageIndex + 1} of {pageOptions.length}
</strong>{" "}
</span>
<span>
| Go to page:{" "}
<input
type="number"
defaultValue={pageIndex + 1}
onChange={(e) => {
const page = e.target.value ? Number(e.target.value) - 1 : 0;
gotoPage(page);
}}
style={{ width: "100px" }}
/>
</span>{" "}
<select
value={pageSize}
onChange={(e) => {
setPageSize(Number(e.target.value));
}}
>
{[10, 20, 30, 40, 50].map((pageSize) => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</select>
<pre>
<code>
{JSON.stringify(
{
selectedRowIds: selectedRowIds,
"selectedFlatRows[].original": selectedFlatRows.map(
(d) => d.original
)
},
null,
2
)}
</code>
</pre>
</div>
</>
);
}
function App() {
const columns = React.useMemo(
() => [
{
Header: "Name",
columns: [
{
Header: "First Name",
accessor: "firstName"
},
{
Header: "Last Name",
accessor: "lastName"
}
]
},
{
Header: "Info",
columns: [
{
Header: "Age",
accessor: "age"
},
{
Header: "Visits",
accessor: "visits"
},
{
Header: "Status",
accessor: "status",
Cell: ({ row }) => (
<div>
<IndeterminateCheckbox2 {...row.getToggleRowSelectedProps()} />
</div>
)
},
{
Header: "Profile Progress",
accessor: "progress"
}
]
}
],
[]
);
const data = React.useMemo(() => makeData(100000), []);
return (
<Styles>
<Table columns={columns} data={data} />
</Styles>
);
}
export default App;
Use React If are you use Uesmemo hooks and pass in the column.
const columns = React.useMemo(
() => [
{
Header: "data",
accessor: "data",
isVisible: true,
disableFilters: true,
Cell: ({ value, row, column }) => {
if (row.index === 0) {
return (
<div>
<Input typy="check"
value={data}
onChange={(e) => {
handleChange(e, "data");
}}
disabled={false}
/>
</div>
);
} else {
return `${row.original}`;
}
},
},
I have a table in my project and I have an edit / view / add page that I can access from this table. My goal is to send the clicked data to the other component without any problem, but no matter how hard I try, I get an undefined error and the project is broken. I would be glad if you could help.
I am sharing my codes from parent to child component
Table page.
import React, { useState, useEffect, useCallback, useMemo } from "react";
import ManagementTable from '../components/ManagementTable'
import {
getApps,
updateStopRisk,
countLiveCountry,
updateAppShow,
deleteApp,
} from "../api/apiCalls";
import VisibilityIcon from "#material-ui/icons/Visibility";
import DeleteIcon from "#material-ui/icons/Delete";
import EditIcon from "#material-ui/icons/Edit";
import Switch from "#material-ui/core/Switch";
import DeleteModal from "../components/DeleteModal";
import { Link } from "react-router-dom";
const Management = () => {
const [apps, setApps] = useState([]);
const [modalVisible, setModalVisible] = useState(false);
const [currentApp, setCurrentApp] = useState("");
const [appID, setAppID] = useState(0);
const fetchData = useCallback(async () => {
const { data: appsResponse } = await getApps();
const countLiveCountries = await fetchLiveCountriesForApps(appsResponse);
setApps(
appsResponse.map((app, idx) => ({
...app,
countLiveCountry: countLiveCountries[idx],
}))
);
}, []);
useEffect(() => {
fetchData();
}, [fetchData]);
const fetchLiveCountriesForApps = async (appwLive) => {
const countLiveCountries = await Promise.all(
appwLive.map((app) => countLiveCountry(app.appID))
);
return countLiveCountries.map(({ data: liveCountries }) => liveCountries);
};
const removeApp = async () => {
await deleteApp(appID);
setModalVisible(false);
fetchData();
};
const onClickCancel = () => {
setModalVisible(false);
};
const columns = useMemo(() => [
{
Header: "Application Name",
accessor: "app_name",
},
{
Header: "Business Area",
accessor: "businessarea.businessarea_name",
},
{
Header: "Live Plants",
accessor: "countLiveCountry",
},
{
Header: "Line Stop Risk",
accessor: "app_stoprisk",
Cell: ({ row: { original } }) => {
const changeCheck = async (id) => {
await updateStopRisk(id);
fetchData();
};
return (
<input
checked={original.app_stoprisk}
onClick={() => {
changeCheck(original.appID);
}}
id="appStopRisk"
type="checkbox"
style={{ width: 18, height: 18, marginTop: 5 }}
/>
)
},
sortType: (a, b, id) => {
if (a.original[id] > b.original[id]) return -1;
if (b.original[id] > a.original[id]) return 1;
},
},
{
Header: "Actions",
Cell: ({ row: { original } }) => {
const changeTrack = async (id) => {
await updateAppShow(id);
fetchData();
};
return (
<>
<Link
className="btn btn-manage-link btn-sm col-2"
to={{
pathname: `/management/${original.app_name}`,
mode: "view",
id: original.appID
}}
>
<VisibilityIcon></VisibilityIcon>
</Link>
<Link
to={{
pathname: `/management/${original.app_name}`,
mode: "edit",
id: original.appID
}}
className="btn btn-manage-link btn-sm col-2"
>
<EditIcon></EditIcon>
</Link>
<button
onClick={() => {
setModalVisible(true);
setCurrentApp(original.app_name);
setAppID(original.appID);
}}
className="btn btn-manage-link btn-sm col-3"
>
<DeleteIcon></DeleteIcon>
</button>
<Switch
onClick={() => changeTrack(original.appID)}
checked={original.app_show}
className="col-3"
></Switch>
</>
)
},
},
],
[fetchData]
);
return (
<div className="container">
<h2 style={{ float: "left", font: "bold" }}>Management</h2>
<div style={{ float: "right" }}>
<Link className="btn btn-danger btn-sm" to={{ pathname: `/management/add`, mode: "add" }}>
Add New App
</Link>
<Link className="btn btn-danger btn-sm ml-3" exact to="/management/plants">
Plant Management
</Link>
</div>
<ManagementTable columns={columns} data={apps} />
<DeleteModal
message={<strong>{currentApp}</strong>}
variety="app"
onClickCancel={onClickCancel}
onClickOk={removeApp}
visible={modalVisible}
/>
</div>
);
};
export default Management;
The page where I transfer the props.
import React, { useState, useEffect } from "react";
import Accordion from "../components/Accordion";
import Details from '../components/Details'
import {
getByIdApps,
} from "../api/apiCalls";
const ApplicationManagement = (props) => {
const [appById, setAppById] = useState([]);
const { id } = props.location;
const [selectOption, setSelectOption] = useState('add')
useEffect(() => {
getData();
getMode();
}, [])
const getData = async () => {
console.log(props.location.id)
if (props.location.id) {
await getByIdApps(props.location.id).then((response) => setAppById(response.data))
console.log(appById)
console.log(props)
}
else {
setSelectOption('add')
}
}
const getMode = () => props.location.mode ? setSelectOption(props.location.mode) : setSelectOption('add')
const handleOptionChange = (event) => {
console.log(event.target.value)
setSelectOption(event.target.value)
}
return (
<>
<div style={{ margin: 20 }}>
<h1>
{appById.app_shortcode} - {appById.app_fullname}
</h1>
<div className="float-right mb-auto">
<label><input type="radio" value="view" checked={selectOption === 'view'} onChange={handleOptionChange} />View</label>
<label> <input type="radio" value="add" checked={selectOption === 'add'} onChange={handleOptionChange} />Add</label>
<label> <input type="radio" value="edit" checked={selectOption === 'edit'} onChange={handleOptionChange} />Edit</label>
</div>
<br></br>
<div style={{ marginLeft: 50, marginRight: 50 }} >
<Accordion
title={
<div style={{ width: 1350 }}>
<h3>Details</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}
content={
<Details appID={id} data = {appById}></Details>
}
/>
<Accordion title={
<div style={{ width: 1350 }}>
<h3>Links</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}></Accordion>
<Accordion title={
<div style={{ width: 1350 }}>
<h3>Factory Management</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}></Accordion>
<Accordion title={
<div style={{ width: 1350 }}>
<h3>Issues Management</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}></Accordion>
<Accordion title={
<div style={{ width: 1350 }}>
<h3>Middleware Management</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}></Accordion>
</div>)
{selectOption === 'add' ? (
<div>
Add Mode
</div>
) : selectOption === 'view' ? (<div>View Mode</div>) : (<div>eidt</div>)}
</div>
</>
);
};
export default ApplicationManagement;
and the section where the details are kept on the ApplicationManagement page (My code is quite long, I just share the problem part.)
import React, { useState, useEffect } from 'react'
import axios from "axios";
import {
getResponsibleTeams,
getBusinessAreas
} from '../api/apiCalls'
const Details = (props) => {
const [rTeams, setrTeams] = useState([]);
const [bAreas, setbAreas] = useState([]);
const { data } = props;
useEffect(() => {
async function fetchData() {
const getrTeams = await getResponsibleTeams();
const getbAreas = await getBusinessAreas();
axios.all([getrTeams, getbAreas]).then(
axios.spread((...allData) => {
const allrTeams = allData[0].data;
const allbAreas = allData[1].data;
setrTeams(allrTeams);
setbAreas(allbAreas);
})
);
}
fetchData();
}, []);
return (
<div>
<div
style={{
float: "left",
width: 1350,
height: 340,
}}
>
<div className="form-group">
<label style={{ float: "left" }} htmlFor="appFullName">
Frontend:{" "}
</label>
<input
id="appFullName"
type="text"
class="form-control"
placeholder="dsfdsdsf"
value={data.frontend.frontend_name} // error here
//onChange={handleInputChange}
name="appShortCode"
style={{ width: 400, marginLeft: 150 }}
/>
</div>
</div>
</div>
)
}
export default Details;
Later I realized that using asynchronous functions caused some problems. I came up with a solution to this and the problem was solved.
Error Code Here :
<Accordion
title={
<div style={{ width: 1350 }}>
<h3>Details</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}
content={
<Details appID={id} data = {appById}></Details>
}
/>
and the solution to the problem
{appById &&
<Accordion
title={
<div style={{ width: 1350 }}>
<h3>Details</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}
content={
<Details appID={id} data = {appById}></Details>
}
/>}
i am using react-table version 7. I am using global filter for filtering, i copied some code from the sandbox and integrated it into mine, this is the link from where i copied the code Open Sandbox. The problem is when ever i write something into the search bar, the whole global filter component is re-rendered and the input field loses focus and every-time, I have to click in the input field again to input text, which is very annoying as it takes time to re-render and i have to click input field everytime. why is this happening and why the same thing does not occur in the sandbox example of the react-table global filters. You can see the example on the above link i have included.
return (
<>
<GlobalFilter
preGlobalFilteredRows={preGlobalFilteredRows}
globalFilter={state.globalFilter}
setGlobalFilter={setGlobalFilter}
/>
{/* //rest of the code*/}
.....
</> );
function GlobalFilter({
preGlobalFilteredRows,
globalFilter,
setGlobalFilter,
}) {
const count = preGlobalFilteredRows.length
return (
<span>
Search:{' '}
<input
value={globalFilter || ''}
onChange={e => {
setGlobalFilter(e.target.value || undefined) // Set undefined to remove the filter entirely
}}
placeholder={`${count} records...`}
style={{
fontSize: '1.1rem',
border: '0',
}}
/>
</span>
)
}
this problem does not occur if i write the code for the global filter directly inside the return statement, using this the whole component does not re-render. like this.
return(
<>
div className="row" style={{ 'zoom': '94%' }}>
<div className="col">
<div class="student-search-field">
<h4>Search: <input
value={state.globalFilter || ""}
onChange={e => {
setGlobalFilter(e.target.value || undefined); // Set undefined to remove the filter entirely
}}
placeholder={` ${count} Students..`}
style={{
fontSize: "1.1rem",
border: "1",
}}
/></h4>
</div>
</div>
</div>
{/* //rest of the code*/}
</>);
The rest of my code is like this
Hers is my complete code.
import React from 'react';
import { useLocation } from "react-router-dom";
import { useTable, usePagination, useGlobalFilter } from 'react-table';
import './classdetails.css';
function SimpleTable(props) {
const location = useLocation();
var paginationDisplay = { display: '' };
console.log(location.state);
const data = React.useMemo(
() => [
{
col1: 'john doe',
col2: "world",
},
{
col1: 'react-table',
col2: 'rocks',
},
{
col1: 'whatever',
col2: 'you want',
},
{
col1: 'whatever',
col2: 'you want',
},
{
col1: 'whatever',
col2: 'you want',
},
{
col1: 'whatever',
col2: 'you want',
},
{
col1: 'whatever',
col2: 'you want',
},
{
col1: 'whatever',
col2: 'you want',
},
{
col1: 'whatever',
col2: 'you want',
},
],
[],
);
const columns = React.useMemo(
() => [
{
Header: 'STUDENT NAME',
accessor: 'col1', // accessor is the "key" in the data
Cell: (row) => {
return (
<>
{row.row.original.col1}
<p>FA16-BCS-067</p>
</>);
},
},
{
Header: 'ATTENDANCE',
accessor: 'col2',// use accessor name in hidden column to hide a column e.g intitalstate.hiddenColumns['col2']
},
{
Header: 'QUIZEZ',
accessor: '',
},
{
Header: 'ASSIGNMENT',
accessor: '',
},
{
Header: 'FIRST TERM',
accessor: '',
},
{
Header: 'MID TERM',
accessor: '',
},
{
Header: 'FINAL TERM',
accessor: '',
},
{
Header: 'action',
accessor: '',
Cell: (row) => {
return (
<button className="btn btn-danger"
onClick={() => console.log(row.row)}
>
View
</button>);
},
},
],
[],
);
function GlobalFilter({
preGlobalFilteredRows,
globalFilter,
setGlobalFilter,
}) {
const count = preGlobalFilteredRows.length
return (
<span>
Search:{' '}
<input
value={globalFilter || ''}
onChange={e => {
setGlobalFilter(e.target.value || undefined) // Set undefined to remove the filter entirely
}}
placeholder={`${count} records...`}
style={{
fontSize: '1.1rem',
border: '0',
}}
/>
</span>
)
}
const {
getTableProps,
getTableBodyProps,
headerGroups,
page,
prepareRow,
canPreviousPage,
canNextPage,
nextPage,
previousPage,
setPageSize,
pageOptions,
state,
preGlobalFilteredRows,
setGlobalFilter,
state: { pageIndex, pageSize }
} = useTable({ columns, data, initialState: { pageIndex: 0, pageSize: 5, hiddenColumns: [''] } },
useGlobalFilter, usePagination);
const count = preGlobalFilteredRows.length;
return (
<>
<GlobalFilter
preGlobalFilteredRows={preGlobalFilteredRows}
globalFilter={state.globalFilter}
setGlobalFilter={setGlobalFilter}
/>
{/* //table section */}
<div className="row">
<div className="col table-div-1 highlight table-2" style={{ 'overflowY': 'auto', 'height': '455px' }}>
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th
{...column.getHeaderProps()}
>
{column.render('Header')}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{page.map(row => {
prepareRow(row)
return (
<tr key={123} {...row.getRowProps()} >
{row.cells.map(cell => {
return (
<td
{...cell.getCellProps()}
onClick={() => console.log()}
>
{cell.render('Cell')}
</td>
)
})}
</tr>
)
})}
</tbody>
</table>
</div>
</div>
{/* //pagination section */}
{ props === props ? <>
<div className="row pagination" style={paginationDisplay}>
<span>
Page{' '}
<strong>
{pageIndex + 1} of {pageOptions.length}
</strong>{' '}
</span>
<button className="btn btn-danger" onClick={() => previousPage()} disabled={!canPreviousPage}>
{'<'}
</button>{" "}
<button className="btn btn-danger" onClick={() => nextPage()} disabled={!canNextPage}>
{'>'}
</button>{" "}
<select className="btn btn-danger"
value={pageSize}
onChange={e => {
setPageSize(Number(e.target.value));
console.log(pageSize);
}}
>
{[5, 10, 20, 30].map(pageSize => (
<option key={pageSize.value} value={pageSize}>
Show {pageSize}
</option>
))}
</select>
</div>
</> : null }
</>
);
}
export default SimpleTable;
I have made the correction of your complete code. You did not use Table function from the Sandbox(from where you copied the code). I tested the following which is working fine and fulfil your requirement. please have a look.
import React from 'react';
import {useTable, usePagination, useGlobalFilter} from 'react-table';
import matchSorter from 'match-sorter'
function SimpleTable(props) {
var paginationDisplay = {display: ''};
const data = React.useMemo(
() => [
{
col1: 'john doe',
col2: "world",
},
{
col1: 'react-table',
col2: 'rocks',
},
{
col1: 'whatever',
col2: 'you want',
},
{
col1: 'whatever',
col2: 'you want',
},
{
col1: 'whatever',
col2: 'you want',
},
{
col1: 'whatever',
col2: 'you want',
},
{
col1: 'whatever',
col2: 'you want',
},
{
col1: 'whatever',
col2: 'you want',
},
{
col1: 'whatever',
col2: 'you want',
},
],
[],
);
const columns = React.useMemo(
() => [
{
Header: 'STUDENT NAME',
accessor: 'col1', // accessor is the "key" in the data
Cell: (row) => {
return (
<>
{row.row.original.col1}
<p>FA16-BCS-067</p>
</>);
},
},
{
Header: 'ATTENDANCE',
accessor: 'col2',// use accessor name in hidden column to hide a column e.g intitalstate.hiddenColumns['col2']
},
{
Header: 'QUIZEZ',
accessor: '',
},
{
Header: 'ASSIGNMENT',
accessor: '',
},
{
Header: 'FIRST TERM',
accessor: '',
},
{
Header: 'MID TERM',
accessor: '',
},
{
Header: 'FINAL TERM',
accessor: '',
},
{
Header: 'action',
accessor: '',
Cell: (row) => {
return (
<button className="btn btn-danger"
onClick={() => console.log(row.row)}
>
View
</button>);
},
},
],
[],
);
function GlobalFilter({
preGlobalFilteredRows,
globalFilter,
setGlobalFilter,
}) {
const count = preGlobalFilteredRows.length
return (
<span>
Search:{' '}
<input
value={globalFilter || ''}
onChange={e => {
setGlobalFilter(e.target.value || undefined) // Set undefined to remove the filter entirely
}}
placeholder={`${count} records...`}
style={{
fontSize: '1.1rem',
border: '0',
}}
/>
</span>
)
}
function fuzzyTextFilterFn(rows, id, filterValue) {
return matchSorter(rows, filterValue, {keys: [row => row.values[id]]})
}
// Our table component
function Table({columns, data}) {
const filterTypes = React.useMemo(
() => ({
// Add a new fuzzyTextFilterFn filter type.
fuzzyText: fuzzyTextFilterFn,
// Or, override the default text filter to use
// "startWith"
text: (rows, id, filterValue) => {
return rows.filter(row => {
const rowValue = row.values[id]
return rowValue !== undefined
? String(rowValue)
.toLowerCase()
.startsWith(String(filterValue).toLowerCase())
: true
})
},
}),
[]
);
const {
getTableProps,
getTableBodyProps,
headerGroups,
page,
prepareRow,
canPreviousPage,
canNextPage,
nextPage,
previousPage,
setPageSize,
pageOptions,
state,
preGlobalFilteredRows,
setGlobalFilter,
state: {pageIndex, pageSize}
} = useTable({columns, data, initialState: {pageIndex: 0, pageSize: 5, hiddenColumns: ['']}},
useGlobalFilter, usePagination);
const count = preGlobalFilteredRows.length;
return (
<>
<GlobalFilter
preGlobalFilteredRows={preGlobalFilteredRows}
globalFilter={state.globalFilter}
setGlobalFilter={setGlobalFilter}
/>
{/* //table section */}
<div className="row">
<div className="col table-div-1 highlight table-2" style={{'overflowY': 'auto', 'height': '455px'}}>
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th
{...column.getHeaderProps()}
>
{column.render('Header')}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{page.map(row => {
prepareRow(row);
return (
<tr key={123} {...row.getRowProps()} >
{row.cells.map(cell => {
return (
<td
{...cell.getCellProps()}
onClick={() => console.log()}
>
{cell.render('Cell')}
</td>
)
})}
</tr>
)
})}
</tbody>
</table>
</div>
</div>
{/* //pagination section */}
{props === props ? <>
<div className="row pagination" style={paginationDisplay}>
<span>
Page{' '}
<strong>
{pageIndex + 1} of {pageOptions.length}
</strong>{' '}
</span>
<button className="btn btn-danger" onClick={() => previousPage()} disabled={!canPreviousPage}>
{'<'}
</button>
{" "}
<button className="btn btn-danger" onClick={() => nextPage()} disabled={!canNextPage}>
{'>'}
</button>
{" "}
<select className="btn btn-danger"
value={pageSize}
onChange={e => {
setPageSize(Number(e.target.value));
console.log(pageSize);
}}
>
{[5, 10, 20, 30].map(pageSize => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</select>
</div>
</> : null}
</>
);
}
return (
<Table columns={columns} data={data}/>
)
}
export default SimpleTable;