Using Firebase Database with React-Table - reactjs

I have some difficulties in trying to make React-Table work with Firebase Database.
I have this logic to get the data from a Firebase DB:
const servicesCollectionRef = collection(db, "service");
const [serviceRequests, setServiceRequest] = useState([]);
const fetchServiceRequest = async () => {
const response = await getDocs(servicesCollectionRef);
const querySnapshot = await getDocs(servicesCollectionRef);
console.log(querySnapshot.docs);
querySnapshot.forEach((doc) => {
console.log(`${doc.id} => ${JSON.stringify(doc.data())}`);
});
setServiceRequest(querySnapshot.docs)
};
useEffect(() => {
fetchServiceRequest();
}, []);
What I don't understand is how can I save the data, because in my console I get this when logging the console.log(querySnapshot.docs):
(2) [Mu, Mu]
0: Mu {_firestore: La, _userDataWriter: dh, _key: Dt, _document: Gt, _converter: null, …}
1: Mu {_firestore: La, _userDataWriter: dh, _key: Dt, _document: Gt, _converter: null, …}
length: 2
[[Prototype]]: Array(0)
How do I have to save my db data or how do i have to acces it in order to also have the columns for React-Table work:
const data = useMemo(() => [...serviceRequests], [serviceRequests]);
const columns = useMemo(
() => [
{
Header: "Cerere",
columns: [
{
Header: "Id",
accessor: "id",
},
{
Header: "Email",
accessor: "Email",
},
{
Header: "Nume",
accessor: "NumeClient",
},
{
Header: "Prenume",
accessor: "PrenumeClient",
},
{
Header: "Pachet",
accessor: "Pachet",
},
{
Header: "Pret",
accessor: "Pret",
},
],
},
{
Header: "Asignare Medic",
columns: [
{
Header: "Medic",
accessor: "Nume",
},
],
},
],
[]
);
// const tableInstance = useTable({ columns, data });
Thank you in advance!

Related

AgGrid cells don't re-render on refresh

This is the cell renderer for the 2 action buttons I'm passing to AgGrid. When I refresh the page, it loses its state and doesn't re-render them, but hits the function nevertheless.
const columnDefs: ColDef[] = useMemo(() => [
{
field: 'actions',
headerName: t('actions', { ns: 'common' }),
cellRendererSelector: (params: ICellRendererParams<IShop>) => ({
component: ActionsCell,
params: {
...params,
options: {
edit: {
id: 'editShop',
hasPermission: user.permissions.canEditShop,
href: (data: IShop) => appRoutes.editShop(router.query.companyId as string, data.id as string)
},
delete: {
id: 'deleteShop',
hasPermission: user.permissions.canDeleteShop,
onDeleteClick: (data: IShop) => handleClickOpen(data),
},
}
}
}),
cellStyle: agGridStyles.actionsCell
},
]
My AgGrid invocation:
<AgGridReact<IShop>
sideBar={sidebarConfig}
ref={gridRef}
containerStyle={agGridStyles.container}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
paginationPageSize={gridPageSize}
onGridReady={onGridReady}
/>
I tried spreading the options, in order to create a new reference to no avail:
options: {
...params.options,
edit: {
//...
}
}
Should I define the options object outside of the cell renderer:
const [options, setOptions] = useState<IOptions>({
edit: {
id: 'editShop',
hasPermission: user.permissions.canEditShop,
href: (data: IShop) => appRoutes.editShop(router.query.companyId as string, data.id as string)
},
delete: {
id: 'deleteShop',
hasPermission: user.permissions.canDeleteShop,
onDeleteClick: (data: IShop) => handleClickOpen(data),
},
});

React Material-UI: Populating Rows in DataGrid returns undefined

In my React application, I want to fill the rows of a Material-UI Data Grid with backend data.
To achieve this, I make a GET request to the backend, which returns an AxiosResponse containing the data.
This AxiosResponse, I store in a variable named list, using useState().
Afterwards, I iterate over list to map the data to an Array of Objects, named testrows.
In the return method with DataGrid, I want to set property row to testrows.
However on rendering, it returns an error saying that testrow is undefined.
What am I doing wrong here?
import ...
export default function FruitList() {
const [list, setList] = useState<AxiosResponse>();
const columns: GridColDef[] = [
{
field: 'id',
headerName: 'Fruit ID',
type: 'number',
},
{
field: 'type',
headerName: 'Fruit Type',
type: 'string',
},
{
field: 'harvest',
headerName: 'Harvest Date',
type: "date",
},
{
field: 'entryDate',
headerName: 'Entry Date',
type: "date",
},
];
// Get list of fruits and store in variable "list"
useEffect(() => {
axios.get('http://localhost:3000/v1/fruit/'
.then(result => {
setList(result);
})
}, [])
let testrows = list?.data.map((element: { id: any, type: any, harvest: any, entryDate: any }) => {
return {
id: element.id,
type: element.type,
harvest: element.harvest,
entryDate: element.entryDate
}
}
);
// also tried useMemo:
const testrows = useMemo(() =>
list?.data.map((element: { id: any, type: any, harvest: any, entryDate: any }) => {
return {
id: element.id,
type: element.type,
harvest: element.harvest,
entryDate: element.entryDate
}
}),
[list?.data]
);
// console output: "test: undefined"
console.log("test: ", testrows);
return (
<div>
<div>
<DataGrid
rows={testrows}
columns={columns}
/>
</div>
</div>
);
}

React: Issue with fetching and updating the state in useCallback

I am currently working on a component that makes an API call, retrieves the data, and then displays the data in the Fluent UI Datalist.
The issue is as follows:
The component loads for the first time, then it re-renders after the API call, and the component shows the correct entries within the table with the state.items being set to correct value. However, when I click on column to run the onColumnClick the items inside the function are empty, which result in an error. The columns are fine, but the state.items is just an empty collection.
How can this possibly be fixed to so that I see the items within the onColumnClick?
Here is a piece of code:
export const ListComponent = (props: ListComponentProps) => {
const fetchPeople = async () => {
const entry: ITableEntry[] = [];
//items ... sdk call
for await (const item of items) {
entry.push({
key: item.id,
name: item.name,
lastName: item.lastname
});
}
}
useEffect(() => {
fetchPeople();
.then(elementList => {
setState(
state => ({ ...state, items: elementList }),
);
});
}, [])
const onColumnClick = React.useCallback((ev: React.MouseEvent<HTMLElement>, column: IColumn): void => {
const columns = state.columns;
const items = state.items;
// PLACE WHERE THE ERROR HAPPENS
console.log(items);
}, []);
const columns: IColumn[] = [
{
key: 'column1',
name: 'First Name',
fieldName: 'name',
minWidth: 210,
maxWidth: 350,
isRowHeader: true,
isResizable: true,
isSorted: true,
isSortedDescending: false,
sortAscendingAriaLabel: 'Sorted A to Z',
sortDescendingAriaLabel: 'Sorted Z to A',
onColumnClick: onColumnClick,
data: 'string',
isPadded: true,
},
{
key: 'column2',
name: 'Last Name',
fieldName: 'lastname',
minWidth: 210,
maxWidth: 350,
isRowHeader: true,
isResizable: true,
isSorted: true,
isSortedDescending: false,
sortAscendingAriaLabel: 'Sorted A to Z',
sortDescendingAriaLabel: 'Sorted Z to A',
onColumnClick: onColumnClick,
data: 'string',
isPadded: true,
},
];
const [state, setState] = React.useState({
items: [] as ITableEntry[],
columns: columns,
});
return (
<>
<DetailsList
items={state.items}
columns={state.columns}
/>
</>
);
});
const onColumnClick = React.useCallback((ev: React.MouseEvent<HTMLElement>, column: IColumn): void => {
const columns = state.columns;
const items = state.items;
// PLACE WHERE THE ERROR HAPPENS
console.log(items);
}, [state]);
add dependency to the use callback to be recalculated when state changes
This is a total rewrite with some notes
import React, {useCallback, useEffect, useState} from "react";
/** Option One if the function does not requires variables from the component
* itself you can put it outside like in "api" folder */
const fetchPeople = async () => {
//items ... sdk call
// if items are already calculated and they are not async
return items.map((item)=>({
key: item.id,
name: item.name,
lastName: item.lastname
}))
// else
// return (await Promise.all(items)).map((item)=>({
// key: item.id,
// name: item.name,
// lastName: item.lastname
// }))
}
export const ListComponent = (props: ListComponentProps) => {
const [items, setItems] = useState<ITableEntry[]>([])
// Option Two: use callback this function is "saved" inside a variable with a memoization based on the
// elements inside the array at the end
// const fetchPeople = useCallback(async () => {
// ...
// }, [])
useEffect(() => {
// option three you can also leave it there so it can be used in other part of the application
// const fetchPeople = async () => {
// ...
// }
// if you like async await toy can run this
(async () => {
setItems(await fetchPeople())
})()
/** if this is not modifiable you don't need to put it there
* and this function will run after the component is "mount"
* in my case fetch people will not change and that is why you should use useCallback
*/
}, [fetchPeople]);
const onColumnClick = useCallback((ev: React.MouseEvent<HTMLElement>, column: IColumn): void => {
console.log(items);
}, [items]);
const columns = [
{
key: 'column1',
name: 'First Name',
fieldName: 'name',
minWidth: 210,
maxWidth: 350,
isRowHeader: true,
isResizable: true,
isSorted: true,
isSortedDescending: false,
sortAscendingAriaLabel: 'Sorted A to Z',
sortDescendingAriaLabel: 'Sorted Z to A',
onColumnClick: onColumnClick,
data: 'string',
isPadded: true,
},
{
key: 'column2',
name: 'Last Name',
fieldName: 'lastname',
minWidth: 210,
maxWidth: 350,
isRowHeader: true,
isResizable: true,
isSorted: true,
isSortedDescending: false,
sortAscendingAriaLabel: 'Sorted A to Z',
sortDescendingAriaLabel: 'Sorted Z to A',
onColumnClick: onColumnClick,
data: 'string',
isPadded: true,
},
]
return (
<>
<DetailsList
items={items}
columns={columns}
/>
</>
);
});
keep variables as simple as possible and unless something strange is required just save "datas" in State
Here is a fix that actually makes this work!
So I actually found a similar post to my issue (although I have searched for it for ages before):
React - function does not print current states
However, the solution had to be modified to this to reflect the changes in the columns.
The solution always also refreshes columns upon changes to items (see useEffects, where I set the columns), so the columns are being updated.
export const ListComponent = (props: ListComponentProps) => {
const [state, setState] = React.useState({
items: [] as IDocument[],
columns: [] as IColumn[],
});
const fetchPeople = React.useCallback(async () => {
const entry: ITableEntry[] = [];
//items ... sdk call
for await (const item of items) {
entry.push({
key: item.id,
name: item.name,
lastName: item.lastname
});
}
setState((state) => ({ ...state, items: elementsList }));
}, []);
useEffect(() => {
setState((state) => ({ ...state, columns: columns }));
}, [state.items]);
useEffect(() => {
fetchPeople();
}, []);
const _onColumnClick = React.useCallback((ev: React.MouseEvent<HTMLElement>, column: IColumn): void => {
const columns = state.columns;
const items = state.items;
console.log(items);
}, [state.items, state.columns]);
const columns: IColumn[] = [
{
key: 'column1',
name: 'First Name',
fieldName: 'name',
minWidth: 210,
maxWidth: 350,
isRowHeader: true,
isResizable: true,
isSorted: true,
isSortedDescending: false,
sortAscendingAriaLabel: 'Sorted A to Z',
sortDescendingAriaLabel: 'Sorted Z to A',
onColumnClick: _onColumnClick,
data: 'string',
isPadded: true,
},
{
key: 'column2',
name: 'Last Name',
fieldName: 'lastname',
minWidth: 210,
maxWidth: 350,
isRowHeader: true,
isResizable: true,
isSorted: true,
isSortedDescending: false,
sortAscendingAriaLabel: 'Sorted A to Z',
sortDescendingAriaLabel: 'Sorted Z to A',
onColumnClick: _onColumnClick,
data: 'string',
isPadded: true,
},
];
return (
<>
<DetailsList
items={state.items}
columns={state.columns}
/>
</>
);
});

Update a selected property from react state of objects with arrays

Assume that this state has initial data like this
const [options, setOptions] = useState({
ProcessType: [
{ value: 1, label: 'Type1' }, { value: 2, label: 'Type2' }
],
ResponsibleUser: [
{ value: 1, label: 'User1' }, { value: 2, label: 'User2' }
]
});
The following function will be called again and again when a post/put called
Help me to complete the commented area as described there.
const fetchProcesses = async () => {
await axios.get(`${process.env.REACT_APP_SERVER_BASE_URL}/processes/`)
.then((result) => {
/*
I want here to clear the existing data in options.ProcessType and
map result.data as { value: result.data.id , label: result.data.name },....
and push/concat it into to options.ProcessType but i want to keep the data
inside options.ResponsibleUser unchanged.
result.data is an array of objects like this,
[
{ id: 1 , name: 'Type1', desc : 'desc1', creator: 3, status: 'active' },
{ id: 2 , name: 'Type2', desc : 'desc2', creator: 6, status: 'closed' },
.....
.....
]
*/
})
}
Here is a solution
const fetchProcesses = async () => {
await axios.get(`${process.env.REACT_APP_SERVER_BASE_URL}/processes/`)
.then((result) => {
// solution
setOptions({ResponsibleUser: [...options.ResponsibleUser], ProcessType: result.data.map(row => ({value: row.id, label: row.name}))})
})
}

Auto Refresh React Table upon success of the delete action server side

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?

Resources