Related
I am fetching data from the database using API and the data will be shown in a graph under 24hours of the current time. the graph will auto refresh at x minutes. I would like to have a notification based on the latest data when it exceed the threshold of the graph. the data that is retrieved from the API will be an object.
code for the graph
const TempGraph = () => {
const [data, setData] = useState([]);
useEffect(() => {
const interval = setInterval(() => asyncFetch(),5000) //5000ms = 5sec
return () => clearInterval(interval) // clear the interval everytime
}, []);
const asyncFetch = () => {
fetch('link')
.then((response) => response.json())
.then((json) => setDatajson))
.catch((error) => {
console.log('fetch data failed', error);
});
};
const config = {
data,
xField: 'time',
yField: 'value',
seriesField:'location',
xAxis: {
title: {
text: 'Hours',
}
},
yAxis:{
title:{
text: 'Temperature in °',
}
},
annotations:[
{
type:'text',
position:['min','35'],
content:'threshold',
offsetY:-3,
style:{
textBaseline:'bottom',
},
},
{
type:'line',
start:['min','35'],
end:['max','35'],
style:{
stroke:'red',
lineDash:[2,2],
},
},
],
meta: {
time: {
alias: 'hours',
},
value: {
alias: 'temperature',
max: 50,
},
},
};
return <Line {...config} />;
}
export default TempGraph;
I am using Material-UI Data-Grid, and I'm running a tutorial grid for server-side data access. This is written in React, and I'm having a problem that the loading circle is permanently spinning. I set a breakpoint in React.useEffect and I see it getting hit over and over and over. handlePageChange doesn't seem to be participating in the infinite loop.
Thinking about this a little, I feel that the state is changing which triggers the whole thing to execute again, hence the loop. What is the condition supposed to be to stop the thing?
import * as React from 'react';
import { DataGrid } from '#material-ui/data-grid';
function loadServerRows(page, data) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(data.rows.slice(page * 5, (page + 1) * 5));
}, Math.random() * 500 + 100); // simulate network latency
});
}
export default function App() {
const data = {
rows: [
{ id: 1, col1: 'Hello', col2: 'World' },
{ id: 2, col1: 'XGrid', col2: 'is Awesome' },
{ id: 3, col1: 'Material-UI', col2: 'is Amazing' },
],
columns: [
{ field: 'col1', headerName: 'Column 1', width: 150 },
{ field: 'col2', headerName: 'Column 2', width: 150 },
],
rowLength: 100,
maxColumns: 6
}
const [page, setPage] = React.useState(0);
const [rows, setRows] = React.useState([]);
const [loading, setLoading] = React.useState(false);
const handlePageChange = (params) => {
setPage(params.page);
};
React.useEffect(() => {
let active = true;
(async () => {
setLoading(true);
const newRows = await loadServerRows(page, data);
if (!active) {
return;
}
setRows(newRows);
setLoading(false);
})();
return () => {
active = false;
};
}, [page, data]);
return (
<div style={{ height: 400, width: '100%' }}>
<DataGrid
rows={rows}
columns={data.columns}
pagination
pageSize={5}
rowCount={100}
paginationMode="server"
onPageChange={handlePageChange}
loading={loading}
/>
</div>
);
}
In your code data changes on each render. As an option, you can store data with useRef (or just make it global moving out of component):
import React from 'react'
import { DataGrid } from '#material-ui/data-grid'
function loadServerRows(page, data) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(data.rows.slice(page * 5, (page + 1) * 5))
}, Math.random() * 500 + 100) // simulate network latency
})
}
function App() {
const data = React.useRef({
rows: [
{ id: 1, col1: 'Hello', col2: 'World' },
{ id: 2, col1: 'XGrid', col2: 'is Awesome' },
{ id: 3, col1: 'Material-UI', col2: 'is Amazing' },
],
columns: [
{ field: 'col1', headerName: 'Column 1', width: 150 },
{ field: 'col2', headerName: 'Column 2', width: 150 },
],
rowLength: 100,
maxColumns: 6
})
const [page, setPage] = React.useState(0)
const [rows, setRows] = React.useState([])
const [loading, setLoading] = React.useState(false)
const handlePageChange = (params) => {
setPage(params.page)
}
React.useEffect(() => {
let active = true;
(async () => {
setLoading(true)
const newRows = await loadServerRows(page, data.current)
if (!active) {
return
}
setRows(newRows)
setLoading(false)
})()
return () => {
active = false
}
}, [page, data])
return (
<div style={{ height: 400, width: '100%' }}>
<DataGrid
rows={rows}
columns={data.current.columns}
pagination
pageSize={5}
rowCount={100}
paginationMode="server"
onPageChange={handlePageChange}
loading={loading}
/>
</div>
)
}
I am trying to implement the draggable columns with react-data-grid based on this example: https://github.com/adazzle/react-data-grid/blob/canary/stories/demos/ColumnsReordering.tsx
I see that this example requires creating a DraggableHeaderRenderer file, so I have copied the following file into my project and converted it to React: https://github.com/adazzle/react-data-grid/blob/canary/stories/demos/components/HeaderRenderers/DraggableHeaderRenderer.tsx
My issue is that I do not know where to import useCombinedRefs from. It is not exported from react-data-grid. I see in the repo that it resides in src/hooks.
I have tried the following:
import {useCombinedRefs} from 'react-data-grid'
// Error: Attempted import error: 'useCombinedRefs' is not exported from 'react-data-grid'.
import {useCombinedRefs} from 'react-data-grid/lib/hooks';
// Error: Module not found: Can't resolve 'react-data-grid/lib/hooks' in 'C:\Users\Liam\Desktop\Work\MyProject\src\ReactDataGrid'
import useCombinedRefs from 'react-data-grid/lib/hooks/useCombinedRefs';
// Error: Module not found: Can't resolve 'react-data-grid/lib/hooks/useCombinedRefs' in 'C:\Users\Liam\Desktop\Work\MyProject\src\ReactDataGrid'
Thanks to anyone who can help.
Here is my code:
DraggableHeaderRenderer.js
import { useDrag, useDrop } from 'react-dnd';
import React from 'react'
import { SortableHeaderCell } from 'react-data-grid';
import useCombinedRefs from 'react-data-grid/lib/hooks/useCombinedRefs';
export function DraggableHeaderRenderer({ onColumnsReorder, column, sortColumn, sortDirection, onSort }) {
const [{ isDragging }, drag] = useDrag({
item: { key: column.key, type: 'COLUMN_DRAG' },
collect: monitor => ({
isDragging: !!monitor.isDragging()
})
});
const [{ isOver }, drop] = useDrop({
accept: 'COLUMN_DRAG',
drop({ key, type }) {
if (type === 'COLUMN_DRAG') {
onColumnsReorder(key, column.key);
}
},
collect: monitor => ({
isOver: !!monitor.isOver(),
canDrop: !!monitor.canDrop()
})
});
return (
<div
ref={useCombinedRefs(drag, drop)}
style={{
opacity: isDragging ? 0.5 : 1,
backgroundColor: isOver ? '#ececec' : 'inherit',
cursor: 'move'
}}
>
<SortableHeaderCell
column={column}
sortColumn={sortColumn}
sortDirection={sortDirection}
onSort={onSort}
>
{column.name}
</SortableHeaderCell>
</div>
);
}
TestDataGrid.js
import React from 'react';
import DataGrid from 'react-data-grid';
import {DraggableHeaderRenderer} from './DraggableHeaderRenderer';
import { useState, useCallback, useMemo } from 'react';
import 'react-data-grid/dist/react-data-grid.css';
const createRows = () => {
const rows = [];
for (let i = 1; i < 500; i++) {
rows.push({
id: i,
task: `Task ${i}`,
complete: Math.min(100, Math.round(Math.random() * 110)),
priority: ['Critical', 'High', 'Medium', 'Low'][Math.round(Math.random() * 3)],
issueType: ['Bug', 'Improvement', 'Epic', 'Story'][Math.round(Math.random() * 3)]
});
}
return rows;
}
const createColumns = () => {
return [
{
key: 'id',
name: 'ID',
width: 80,
},
{
key: 'task',
name: 'Title',
resizable: true,
sortable: true,
draggable: true
},
{
key: 'priority',
name: 'Priority',
resizable: true,
sortable: true,
draggable: true
},
{
key: 'issueType',
name: 'Issue Type',
resizable: true,
sortable: true,
draggable: true
},
{
key: 'complete',
name: '% Complete',
resizable: true,
sortable: true,
draggable: true
}
];
}
export default function TestDataGrid() {
const [rows] = useState(createRows)
const [columns, setColumns] = useState(createColumns)
const draggableColumns = useMemo(() => {
const HeaderRenderer = (props) => {
return <DraggableHeaderRenderer {...props} onColumnsReorder={handleColumnsReorder}/>
}
const handleColumnsReorder = (sourceKey, targetKey) => {
const sourceColumnIndex = columns.findIndex(c => c.key === sourceKey);
const targetColumnIndex = columns.findIndex(c => c.key === targetKey);
const reorderedColumns = [...columns];
reorderedColumns.splice(
targetColumnIndex,
0,
reorderedColumns.splice(sourceColumnIndex, 1)[0]
);
setColumns(reorderedColumns);
}
return columns.map(c => {
if(c.key === "id") return c;
return {...c, HeaderRenderer}
});
}, [columns])
return (
<DataGrid
columns={draggableColumns}
rows={rows}
/>
);
}
How can i refresh the react-table upon editing/deleting a data (server-side based), Here's my code so far
import React,{useState,useEffect} from 'react'
import { useTable, usePagination } from 'react-table'
// import {TableContainer} from './?'
import Table from './TableContainer'
import axios from 'axios';
// Let's add a fetchData method to our Table component that will be used to fetch
// new data when pagination state changes
// We can also add a loading state to let our table know it's loading new data
function App() {
const columns = React.useMemo(
() => [
{
Header: 'Name',
columns: [
{
Header: 'name',
accessor: 'name',
},
{
Header: 'Last Name',
accessor: 'lastName',
},
],
},
{
Header: 'Info',
columns: [
{
Header: 'Age',
accessor: 'age',
},
{
Header: 'Visits',
accessor: 'visits',
},
{
Header: 'Status',
accessor: 'status',
},
{
Header: 'Profile Progress',
accessor: 'progress',
},
{
Header: 'Action',
accessor: '',
id: 'edit',
accessor: 'id',
Cell: ({value}) => (<button onClick={() => editData(value)} data-id={value}>Edit</button>)
},
],
},
],
[]
)
// We'll start our table without any data
const [data, setData] = React.useState([])
const [loading, setLoading] = React.useState(false)
const [pageCount, setPageCount] = React.useState(0)
const [ totalPages, setTotalPages ] = useState(0);
const [ sizePerPage, setSizePerPage ] = useState(10);
const [ page, setPage ] = useState(0);
const [ filtered, setFiltered ] = useState("");
const fetchIdRef = React.useRef(0)
const fetchData = React.useCallback(({ pageSize, pageIndex,filtered }) => {
console.log(filtered);
setLoading(true);
axios.get(`http://127.0.0.1:8000/api/schools?q=${filtered}&sizePerPage=${pageSize}&page=${pageIndex+1}`)
.then((res)=> {
setData(res.data.data);
setSizePerPage(res.data.meta.per_page)
setPage(res.data.meta.current_page)
setPageCount(Math.ceil(res.data.meta.total / pageSize))
setLoading(false)
});
}, [])
function editData(name) {
setLoading(true);
console.log(name)
console.log(sizePerPage);
axios.delete(`http://127.0.0.1:8000/api/schools/${name}`)
.then((res) =>{
// fetchData(sizePerPage);
})
}
return (
<Table
columns={columns}
data={data}
fetchData={fetchData}
loading={loading}
pageCount={pageCount}
filtered={filtered}
filterable
/>
)
}
export default App
When I call the fetch data upon the response, it can't access the callback param of pageSize,pageIndex, filtered I've already try to call it with params but it gives me an error that the pageSize,pageIndex are now undefined
What am I doing wrong?
I would like to implement styling for cells based on values. If a cell has a value within a range, it should have different background colors.
Here is my implementation of the child component which just gets me the columns and does the sort.
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {sortColName} from '../../actions/index';
class DashboardColumns extends Component {
componentDidMount() {
this.props.onRef(this);
}
componentWillUnmount() {
this.props.onRef(undefined);
}
columnClick = (dataField) => {
const sortField = {
sortColName: dataField,
sortDir: this.props.sortColDir === 'asc' ? 'desc' : 'asc',
};
this.props.sortColName(sortField);
};
sortFormatter = (label, column) => {
if (column === this.props.sortCol) {
if (this.props.sortColDir === 'asc') {
return <i><span className="glyphicon glyphicon-triangle-top" />{label}</i>;
} else return <i><span className="glyphicon glyphicon-triangle-bottom" />{label}</i>;
}
return label;
};
percentageFormatter = cell => (<span>{cell} %</span>);
styleFormatter = (cell) => {
if (cell >= 95) {
return <span className="green-background">{cell}</span>;
} else if (cell < 95 && cell > 79) {
return <span className="yellow-background">{cell}</span>;
}
return <span className="red-background">{cell}</span>;
};
columns = [
{
property: 'database_name',
header: {
label: 'Database Name',
formatters: [label => this.sortFormatter(label, 'db_name')],
transforms: [
property => ({
onClick: () => this.columnClick('db_name')
})
]
}
}, {
property: 'target_address',
header: {
label: 'Target Address',
formatters: [label => this.sortFormatter(label, 'target_address')],
transforms: [
property => ({
onClick: () => this.columnClick('target_address')
})
]
}
}, {
property: 'db_type',
header: {
label: 'Database Type',
formatters: [label => this.sortFormatter(label, 'db_type')],
transforms: [
property => ({
onClick: () => this.columnClick('db_type')
})
]
}
}, {
property: 'environment_classification',
header: {
label: 'Environment',
formatters: [label => this.sortFormatter(label, 'environment_classification')],
transforms: [
property => ({
onClick: () => this.columnClick('environment_classification')
})
]
}
}, {
property: 'non_comp_acc',
header: {
label: '# of Non-Compliant Accounts',
formatters: [label => this.sortFormatter(label, 'non_compliant')],
transforms: [
property => ({
onClick: () => this.columnClick('non_compliant')
})
]
}
}, {
property: 'comp_acc',
header: {
label: '# of Compliant Accounts',
formatters: [label => this.sortFormatter(label, 'compliant')],
transforms: [
property => ({
onClick: () => this.columnClick('compliant')
})
]
}
}, {
property: 'percentage_compliant',
header: {
label: 'Percentage Compliant',
formatters: [label => this.sortFormatter(label, 'percentage_compliant')],
transforms: [
property => ({
onClick: () => this.columnClick('percentage_compliant')
})
]
},
cell: {
formatters: [this.percentageFormatter],
transforms: [
value => this.styleFormatter(value)
]
}
}];
render() {
return null;
}
}
const mapStateToProps = state => ({
sortCol: state.getSortColName.sort.sortColName,
sortColDir: state.getSortColName.sort.sortDir,
});
const mapDispatchToProps = dispatch => ({
sortColName: dataField => dispatch(sortColName(dataField)),
});
export default connect(mapStateToProps, mapDispatchToProps)(DashboardColumns);
Please advice. Also could someone help me out on the formatters stuff. I am getting what I want perfectly but is there a more cleaner code for this?
Set a transform like this
styleTransform = (cell) => {
if (cell >= 95) {
return { className: 'green-background' }
}
...
};
Issue at tracker: https://github.com/reactabular/reactabular/issues/341 .