Search Input for ANTD Table - reactjs

I've managed to make a search input that allow search for the title and category of the project from the antd table but the initial data {dataSource} is not loaded with the data in dataLog (not sure is it because of AJAX request) and thus not loaded into the table, the data will only be shown when the first Search is performed at this case, here is my code:
const ListLogs = () => {
const [logs, setLogs] = useState([]);
const [search, setSearch] = useState("");
// const [latestFive, setLatestFive] = useState([]);
const [value, setValue] = useState("");
const timeAgo = (prevDate) => {
const diff = Number(new Date()) - prevDate;
const minute = 60 * 1000;
const hour = minute * 60;
const day = hour * 24;
const month = day * 30;
const year = day * 365;
switch (true) {
case diff < minute:
const seconds = Math.round(diff / 1000);
return `${seconds} ${seconds > 1 ? "seconds" : "second"} ago`;
case diff < hour:
return Math.round(diff / minute) + " minutes ago";
case diff < day:
return Math.round(diff / hour) + " hours ago";
case diff < month:
return Math.round(diff / day) + " days ago";
case diff < year:
return Math.round(diff / month) + " months ago";
case diff > year:
return Math.round(diff / year) + " years ago";
default:
return "";
}
};
const getAllLogs = async () => {
try {
const response = await fetch("http://localhost:5000/logs/");
const jsonData = await response.json();
setLogs(jsonData);
} catch (err) {
console.error(err.message);
}
};
const expandedRowRender = () => {
const columns = [
{ title: "Date", dataIndex: "date", key: "date" },
{ title: "Name", dataIndex: "name", key: "name" },
{
title: "Status",
key: "state",
render: () => (
<span>
<Badge status="success" />
Finished
</span>
),
},
{ title: "Upgrade Status", dataIndex: "upgradeNum", key: "upgradeNum" },
{
title: "Type",
dataIndex: "operation",
key: "operation",
render: () => {
return (
<div>
<Tag color="green">CREATE</Tag>
<Tag color="gold">UPDATE</Tag>
<Tag color="red">DELETE</Tag>
</div>
);
},
},
];
const data = [];
for (let i = 0; i < 3; ++i) {
data.push({
key: i,
date: "2014-12-24 23:12:00",
name: "This is production name",
upgradeNum: "Upgraded: 56",
});
}
return <Table columns={columns} dataSource={data} />;
};
const dataLog = [];
for (let i = 0; i < logs.length; i++) {
dataLog.push({
key: i,
category: <Tag color="default">{logs[i].category}</Tag>,
title: logs[i].title,
id: logs[i].id,
lastUpdated:
new Date(logs[i].last_updated).toLocaleString() +
" " +
timeAgo(new Date(logs[i].last_updated).getTime()),
});
}
const [dataSource, setDataSource] = useState(dataLog);
console.log("dataSource: ", dataSource);
console.log("dataLog: ", dataLog);
const columns = [
{
title: "Category",
dataIndex: "category",
key: "category",
},
{
title: "Title",
dataIndex: "title",
key: "title",
},
{ title: "ID", dataIndex: "id", key: "id" },
{ title: "Last Updated", dataIndex: "lastUpdated", key: "lastUpdated" },
];
useEffect(() => {
setDataSource(dataLog);
getAllLogs();
}, []);
return (
<Fragment>
<div>
<header className="headerPage">
<h1> Logs </h1>
</header>
</div>
<div className="container">
<Input.Search
placeholder="Input search text"
value={value}
onChange={(e) => {
const currValue = e.target.value;
setValue(currValue);
const filteredData = dataLog.filter(
(entry) =>
entry.title.toLowerCase().includes(currValue.toLowerCase()) ||
entry.category.props.children
.toLowerCase()
.includes(currValue.toLowerCase())
);
console.log("filtered Data: ", filteredData);
setDataSource(filteredData);
}}
// allowClear
// onChange={(e) => setSearch(e.target.value)}
// style={{ width: 200, float: "right" }}
/>
<Table
bordered
className="components-table-demo-nested"
onRow={(i) => ({
onClick: (e) => {
history.push(
`/admin/viewLog/${i.id}/${i.category.props.children}`
);
},
})}
columns={columns}
// expandable={{ expandedRowRender }}
dataSource={dataSource} //tried {dataSource ? dataSource: dataLog} does not work as well
size="small"
pagination={false}
/>
</div>
</Fragment>
);
};
export default ListLogs;
Please enlighten me for this! Thank you

Short answer:
When we call setDataSource(dataLog) inside the useEffect hooks for initial render (mounting) that would save empty array in dataSource variable, which can be used for showing empty list icon or loading, we have to set data source again when the data is fetched so, we can use another useEffect hook for logs state variable and set data source in it.
useEffect(() => {
setDataSource(dataLog);
}, [logs]);
Details:
What we are trying to achieve with these hooks i.e. useEffect, is that we can write a code which can react when defined action occur, e.g. what we did in useEffect is that on initial render (mount phase), we set empty array and then call the API. After that when data arrives we set logs i.e. setLogs(jsonData).
Which will populate the logs variable, then that loop come into play and dataLog will get filled but after that we never set the data source again with this DataLog i.e. filled list of objects (we only did that in for mount phase in which dataLog was empty, or when onChange get triggered)
So, a simple solution can be to use useEffect hooks for logs variable so, whenever the logs variable change, it will set the data source as well. As defined above in short answer.
Thus, with these hooks, we can significantly refactor this code.
One More Thing:
I recommend using getAllLogs function with await keyword, that will make that async code works like sync one i.e.
useEffect(async () => {
await getAllLogs();
}, []);
I (try to) reproduce it here

Related

Array in React State is overwritten when handling mulitple files

I have a system where, once the user selects an Excel file, the file is read into state MultipleFileTableData.ExcelData and is then shown using AntD table.
When the user selects a file, the file is uploaded to an API which returns the original filename and the filename as stored in the system.
Once the file is uploaded, the data is then updated to indicate that the file has uploaded and the filename as stored in the system is added.
The above works perfectly fine if a single file is selected, however the system needs to allow multiple files. As soon as you select more than one file to upload, the state is overwritten. In short, the first file row is updated with the flag for file uploaded and the stored filename. But then once the second file is processed, the first file reverts.
I've omitted most of the code as I beleive it is not relevant. Code below:
const CertificateUploadMultiple = ({FormState, SetFormState}) => {
const { Dragger } = Upload;
const [MultipleFileTableData, SetMultipleFileTableData] = useState({ExcelData: []});// userRef([]); // This holds the data from the imported excel file
const BatchUploadTableColumns = [
{title: 'Customer', dataIndex: 'Customer Name', key: 'Customer Name'},
{title: 'Serial Number', dataIndex: 'Serial Number', key: 'Serial Number'},
{title: 'Certificate Number', dataIndex: 'Certificate Number', key: 'Certificate Number'},
{title: 'Certificate Date', dataIndex: 'Certificate Date', key: 'Certificate Date'},
{title: 'Certificate File', dataIndex: 'Certificate Filename', key: 'Certificate Filename'},
{title: 'Certificate Uploaded', align: 'right', render: (text, record, index) => {
return (<>
{record['FileUploaded'] == 'Y' ? <span>File uploaded</span> : <span>Awaiting File</span>}
<Button danger size='small' style={{marginLeft: "0.5em"}} onClick={() => {DeleteExcelRowData(text, record, index);}}><DeleteOutlined /></Button>
</>)
}
},
]
const ReadTemplateFile = async (e) => {
console.log("ReadTemplateFile", e);
const AllowedFiles = ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'];
if (!AllowedFiles.includes(e.type)) {
let Content = <><p>'{file.name}' is not a permitted file</p><p>Received File type: '{file.type}'</p></>;
Modal.error({
title: 'Invalid File',
content: Content
})
return false;
}
const reader = new FileReader();
reader.onload = (evt) => {
const bstr = evt.target.result;
const wb = XLSX.read(bstr, { type: "binary" });
const wsname = wb.SheetNames[0];
const ws = wb.Sheets[wsname];
const RawData = XLSX.utils.sheet_to_json(ws);
console.log("Excel Raw Data", RawData);
const Rows = RawData.map((row, idx) => ({...row, key:idx, FileUploaded: 'N', UploadedFileName: ''}));
SetMultipleFileState(prevState => ({...prevState, ShowTemplateUpload: false}));
SetMultipleFileTableData(prevState => ({...prevState, ExcelData: Rows}));
};
reader.readAsBinaryString(e);
return false;
}
const RowColour = (record) => {
//if (UploadedFileList.includes(record['Certificate Filename'])){
//if (UploadedFileListRef.current.includes(record['Certificate Filename'])){
/*if (record.FileUploaded == 'Y'){
return 'fileuploaded';
}*/
return record.FileUploaded === 'Y' ? 'fileuploaded' : 'awaitingfile';
//return 'awaitingfile';
}
const MultipleCertificateFileUploadProcessFile = async (options) => {
const { onSuccess, onError, file, onProgress } = options;
const UploadProgressHandler = (event) => {
const percent = Math.floor((event.loaded / event.total) * 100);
setProgress(percent);
if (percent === 100) {
setTimeout(() => setProgress(0), 1000);
}
onProgress({ percent: (event.loaded / event.total) * 100 });
}
const Data = new FormData();
Data.append("CertificateFile", file);
try {
CertificateService.UploadCertificateFile(Data, UploadProgressHandler)
.then((resp) => {
if (resp.data.code == '200'){
// mark the line as completed
/*let idx = MultipleFileTableData.ExcelData.findIndex((obj => obj["Certificate Filename"] === resp.data.UploadedOriginalFileName));
console.log("idx", idx);
let t = MultipleFileTableData.ExcelData[idx];
t.FileUploaded = 'Y';
t.UploadedFileName = resp.data.UploadedFileName;*/
//SetMultipleFileTableData(prevState => ({...prevState, ExcelData: [...prevState.ExcelData, t]}));
let t = MultipleFileTableData.ExcelData.map(p => p["Certificate Filename"] === resp.data.UploadedOriginalFileName ?
{...p, FileUploaded: 'Y', UploadedFileName: resp.data.UploadedFileName}
: p
);
SetMultipleFileTableData(prevState => ({...prevState, ExcelData: t}));
onSuccess("Ok");
} else {
message.error(<div>Unable to process file '{resp.data.CertificateFileName}': {resp.data.msg}</div>)
console.log("Error - ", resp.data);
onError(resp.data);
}
console.log("resp", resp);
});
} catch (err) {
console.log( err);
onError(err);
}
}
Here is the visual part
<Row>
<Col span={20}>
<Table
// dataSource={MultipleFileState.ExcelFileData}
dataSource={MultipleFileTableData.ExcelData}
columns={BatchUploadTableColumns}
rowClassName={RowColour}
pagination={false}
/>
</Col>
<Col span={4}>
<Dragger
name='BatchCertificateFile'
multiple={true}
beforeUpload={MultipleCertificateFileBeforeUpload}
style={{marginLeft: "1em"}}
onChange={MultipleCertificateFileUploadOnChange}
customRequest={MultipleCertificateFileUploadProcessFile}
showUploadList={true}
fileList={MultipleFileState.MultipleCertificateFileList}
>
<p className="ant-upload-drag-icon">
<InboxOutlined />
</p>
<p className="ant-upload-text">Click or drag certificate files to this area to start processing</p>
</Dragger>
</Col>
</Row>
OK so, to prevent this I needed to stop mutating the state with the current state value. This is what was causing the value to be overwritten. Turns out that I should have moved the MultipleFileTableData.ExcelData.map to within the SetMultipleFileTableData and instead use the prevState variable.
SetMultipleFileTableData(prevState => ({...prevState, ExcelData:
(
prevState.ExcelData.map(p => p["Certificate Filename"] === resp.data.UploadedOriginalFileName ?
{...p, FileUploaded: 'Y', UploadedFileName: resp.data.UploadedFileName}
: p
)
)
}));

REACT- Displaying and filtering specific data

I want to display by default only data where the status are Pending and Not started. For now, all data are displayed in my Table with
these status: Good,Pending, Not started (see the picture).
But I also want to have the possibility to see the Good status either by creating next to the Apply button a toggle switch : Show good menus, ( I've made a function Toggle.jsx), which will offer the possibility to see all status included Good.
I really don't know how to do that, here what I have now :
export default function MenuDisplay() {
const { menuId } = useParams();
const [selected, setSelected] = useState({});
const [hidden, setHidden] = useState({});
const [menus, setMenus] = useState([]);
useEffect(() => {
axios.post(url,{menuId:parseInt(menuId)})
.then(res => {
console.log(res)
setMenus(res.data.menus)
})
.catch(err => {
console.log(err)
})
}, [menuId]);
// If any row is selected, the button should be in the Apply state
// else it should be in the Cancel state
const buttonMode = Object.values(selected).some((isSelected) => isSelected)
? "apply"
: "cancel";
const rowSelectHandler = (id) => (checked) => {
setSelected((selected) => ({
...selected,
[id]: checked
}));
};
const handleClick = () => {
if (buttonMode === "apply") {
// Hide currently selected items
const currentlySelected = {};
Object.entries(selected).forEach(([id, isSelected]) => {
if (isSelected) {
currentlySelected[id] = isSelected;
}
});
setHidden({ ...hidden, ...currentlySelected });
// Clear all selection
const newSelected = {};
Object.keys(selected).forEach((id) => {
newSelected[id] = false;
});
setSelected(newSelected);
} else {
// Select all currently hidden items
const currentlyHidden = {};
Object.entries(hidden).forEach(([id, isHidden]) => {
if (isHidden) {
currentlyHidden[id] = isHidden;
}
});
setSelected({ ...selected, ...currentlyHidden });
// Clear all hidden items
const newHidden = {};
Object.keys(hidden).forEach((id) => {
newHidden[id] = false;
});
setHidden(newHidden);
}
};
const matchData = (
menus.filter(({ _id }) => {
return !hidden[_id];
});
const getRowProps = (row) => {
return {
style: {
backgroundColor: selected[row.values.id] ? "lightgrey" : "white"
}
};
};
const data = [
{
Header: "id",
accessor: (row) => row._id
},
{
Header: "Name",
accessor: (row) => (
<Link to={{ pathname: `/menu/${menuId}/${row._id}` }}>{row.name}</Link>
)
},
{
Header: "Description",
//check current row is in hidden rows or not
accessor: (row) => row.description
},
{
Header: "Status",
accessor: (row) => row.status
},
{
Header: "Dishes",
//check current row is in hidden rows or not
accessor: (row) => row.dishes,
id: "dishes",
Cell: ({ value }) => value && Object.values(value[0]).join(", ")
},
{
Header: "Show",
accessor: (row) => (
<Toggle
value={selected[row._id]}
onChange={rowSelectHandler(row._id)}
/>
)
}
];
const initialState = {
sortBy: [
{ desc: false, id: "id" },
{ desc: false, id: "description" }
],
hiddenColumns: ["dishes", "id"]
};
return (
<div>
<button type="button" onClick={handleClick}>
{buttonMode === "cancel" ? "Cancel" : "Apply"}
</button>
<Table
data={matchData}
columns={data}
initialState={initialState}
withCellBorder
withRowBorder
withSorting
withPagination
rowProps={getRowProps}
/>
</div>
);
}
Here my json from my api for menuId:1:
[
{
"menuId": 1,
"_id": "123ml66",
"name": "Pea Soup",
"description": "Creamy pea soup topped with melted cheese and sourdough croutons.",
"dishes": [
{
"meat": "N/A",
"vegetables": "pea"
}
],
"taste": "Good",
"comments": "3/4",
"price": "Low",
"availability": 0,
"trust": 1,
"status": "Pending",
"apply": 1
},
//...other data
]
Here my CodeSandbox
Here a picture to get the idea:
Here's the second solution I proposed in the comment:
// Setting up toggle button state
const [showGood, setShowGood] = useState(false);
const [menus, setMenus] = useState([]);
// Simulate fetch data from API
useEffect(() => {
async function fetchData() {
// After fetching data with axios or fetch api
// We process the data
const goodMenus = dataFromAPI.filter((i) => i.taste === "Good");
const restOfMenus = dataFromAPI.filter((i) => i.taste !== "Good");
// Combine two arrays into one using spread operator
// Put the good ones to the front of the array
setMenus([...goodMenus, ...restOfMenus]);
}
fetchData();
}, []);
return (
<div>
// Create a checkbox (you can change it to a toggle button)
<input type="checkbox" onChange={() => setShowGood(!showGood)} />
// Conditionally pass in menu data based on the value of toggle button "showGood"
<Table
data={showGood ? menus : menus.filter((i) => i.taste !== "Good")}
/>
</div>
);
On ternary operator and filter function:
showGood ? menus : menus.filter((i) => i.taste !== "Good")
If button is checked, then showGood's value is true, and all data is passed down to the table, but the good ones will be displayed first, since we have processed it right after the data is fetched, otherwise, the menus that doesn't have good status is shown to the UI.
See sandbox for the simple demo.

dynamic antd table title - react

I am using antd table here, I successfully populated one of the title vertically and I wanna populate the other one horizontally, refer to the images for better clarification thanks
here is my table column:-
const columns = [
{
title: 'Days',
dataIndex: 'date',
defaultSorter: 'ascend',
key: 'title',
sorter: (a, b) => a.date.localeCompare(b.date),
sortDirections: ['descend', 'ascend'],
render: (date) => getDayName(new Date(date)),
},
{
title: 'period',
dataIndex: 'period',
},
}
Datasource or data from api
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [query, setQuery] = useState('');
const fetchData = async () => {
setLoading(true);
const { data } = await getTimetable();
setData(data);
setLoading(false);
};
useEffect(() => {
fetchData();
}, []);
rendering table data:-
<TableContainer columns={columns} rowKey={(record) => record.login.uuid} dataSource={data} />
What I want to achieve :point_down:
what I have right now:-
In antd Table, columns need to passed as below format.
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
},
// .... rest of the cols
];
So to create dynamic columns from the response data you get here, 1st you need to create the array of objects (cols) as the above format that need to pass for <Table columns={formattedColsfromResponseData}.
So in your case you need to create columns[] like below format.
let cols = [
{
title: "Days",
dataIndex: "date",
defaultSorter: "ascend",
key: "title",
sorter: (a, b) => a.date.localeCompare(b.date),
sortDirections: ["descend", "ascend"]
render: (date) => getDayName(new Date(date)),
},
{
title: "period 1"
key: "period 1"
render: (row) => row.section + " - " + row.subject; // render function for customized data.
},
{
title: "period 2"
key: "period 2"
render: (row) => row.section + " - " + row.subject; // render function for customized data
},
// .... rest of the 'Period n' cols in the response.
];
With the below method you can create cols as required format by passing the response data.
This method works assuming response data has only unique 'Periods'.
const generateColumns = (data) => {
let cols = [];
const days = {
title: "Days",
dataIndex: "date",
defaultSorter: "ascend",
key: "title",
sorter: (a, b) => a.date.localeCompare(b.date),
sortDirections: ["descend", "ascend"]
render: (date) => getDayName(new Date(date)),
};
cols.push(days); // add 'Days' obj to Columns.
// for render: property in cols need to return a function. Here creates that.
const generateRender = (row) => {
console.log("gen row----", row);
return (row) => row.section + " - " + row.subject;
};
// create the col objects for each 'Period'. This method works assuming response data has only unique 'Periods'.
data.map((row) => {
let period = {}; // for create 'Period' obj for cols.
period["title"] = row.period;
period["key"] = row.period;
period["render"] = generateRender(row); // only need this if you render customized data.
cols.push(period); // add Current Period obj to Columns.
});
return cols;
};
Now you can pass this method to columns prop in Table with response data which returns the dynamic cols.
const App = () => (
<div>
<Table columns={generateColumns(data)} dataSource={data} />
</div>
);
Check this full demo antd-dynamic-cols-example code.

Replacing value in nested state with react functional components

I am trying to use update state in a react function component but it is not working. I tried following a tutorial on pluralsite and apply it to my own project. Ideally this code should be finding the product based on the ID number and replacing the total with a new value.
Unfortunately I am getting an error saying that 'productData.find' is not a function and I'm not sure where the code being used for that is. Are there any suggestions on how to solve this issue?
This is what the data looks like. In this example I am saving the first element of the array.
export let data = [
{
name: "Name",
description:
"",
products: [
{
id: 1,
name: "Name 1",
material: 1.05,
time: 25,
total: 0,
},
{
id: 2,
name: "Name 2",
material: 3,
time: 252,
total: 0,
},
],
},
...
];
function CompareCard({}) {
const index = 0;
const [productData, setProductData] = useState(data[index]);
function setTotalUpdate(id) {
const productPrevious = productData.find(function (rec) {
return rec.id === id;
});
const productUpdated = {
...productPrevious,
total: 1,
};
const productNew = productData.map(function (rec) {
return rec.id === id ? productUpdated : rec;
});
setProductData(productNew);
}
setTotalUpdate(1)
}
It's because productData is not an array so .find would not work. You want iterate over the products property in your data, so do productData.products.find(...)
When you do
const [productData, setProductData] = useState(data[index])
you don't pass an Array on your state but an Object (the first element of your data so an Object) and Object don't have find method.
Try
const [productData, setProductData] = useState([data[index]])
with [] on our useState to put your Object on array
///////////////////////////////// Edit /////////////
Ok, I try your code, and I propose you this.
import React, { useState } from "react";
const data = [
{
name: "Name",
description: "",
products: [
{
id: 1,
name: "Name 1",
material: 1.05,
time: 25,
total: 0,
},
{
id: 2,
name: "Name 2",
material: 3,
time: 252,
total: 0,
},
],
},
];
const CompareCard = () => {
// setState with the subArray products from data[0], I use '...' (spread operator) inside a Array or an Object to make a shalow copy
const [productsData, setProductsData] = useState([...data[0].products]);
const setTotalUpdate = (id) => {
// find the product to change inside products collection, that's ok
const productPrevious = productsData.find((rec) => {
return rec.id === id;
});
// create a new product to change one property's value, you can do something like 'productPrevious.total = 1', same same
const productUpdated = {
...productPrevious,
total: 1,
};
// create a new products collection to update state
const productNew = productsData.map((rec) => {
return rec.id === id ? productUpdated : rec;
});
setProductsData([...productNew]);
};
const setTotalUpdateSecond = (id) => {
// create a newState
const newState = productsData.map((product) => {
// condition if id === productId and do something
if (id === product.id) {
product.total = 1;
}
// both case, I return the product (change or not)
return product;
});
setProductsData([...newState]);
};
return (
<>
<button onClick={() => setTotalUpdate(1)}>Test old method on product 1</button>
<button onClick={() => setTotalUpdateSecond(2)}>Test second method on product 2</button>
{productsData.map((product) => {
return (
<>
<p>Product Id : {product.id} / Product Total : {product.total}</p>
</>
);
})}
</>
);
};
export default CompareCard;
Can you copy / past this, try and say me if it's what you want, if yes, I explain you where the confusion was. If not, explain me, what's the problem here and I modificate.

In react-table with React Hooks, how to make a get request inside Cell?

I have been working with React-table for a couple of days now, my first time using it, and I have ran into some issues I can't quite seem to resolve. I am trying to build a table where I can show data from two API get requests at the same time, and since I don't know if there is a way to connect the two requests data into one object, and I wouldn't know how to do it, I was trying to access some of the data with get requests inside the react-table Column Cell itself.
My case being: I have two objects, Contacts and Institutions, contacts have in their data the institution ID as parameter, and I need to show in the table both the contact information and some information of the institution that is linked to it, getting it from the institution ID that is present in the contact data.
Here is one example of contact:
{
"contact_id": "34378a25-fe8c-4c64-bd35-59eab3f30863",
"institution_id": "ae1d0fe8-cce1-40ef-87d7-729dfbe9716d",
"name": "Contato 2",
"role": "Cargo 1",
"phone_numbers": [],
"emails": [],
"createdAt": "2021-03-09T20:40:26.6863764Z",
"updatedAt": "2021-03-09T20:40:26.686376448Z",
"deleted": false
}
And here is the institution data:
{
"institution_id": "ae1d0fe8-cce1-40ef-87d7-729dfbe9716d",
"name": "Matheus Salles Blanco",
"socialReason": "teste",
"cnpj": "99999999999999",
"abbreviation": "Matheus",
"website": "teste.com",
}
This is the code that is being implemented, reduced to only the parts that matter and that is working, but only showing the info that is being fetched from the contact object:
const Contacts = ({ match }) => {
const [data, setData] = useState([]);
const [institution, setInstitution] = useState();
const dataRecoil = useRecoilValue(contactData);
const handleContact = useCallback(async () => {
const response = dataRecoil.data;
if (response) {
setData(response.filter((contact) => !contact.deleted));
}
}, [setData, dataRecoil]);
useEffect(() => {
handleContact();
}, [handleContact]);
const columns = useMemo(
() => [
{
Header: 'Nome',
accessor: 'name',
},
{
Header: 'Sigla',
accessor: 'abbreviation',
},
{
Header: 'Nome Fantasia',
accessor: 'institution_id',
},
],
[editToggle, handleDelete],
);
return (
<>
<Table columns={columns} data={data} />
</>
);
};
And a print of it:
And here is what I have been trying to do:
const Contacts = ({ match }) => {
const [data, setData] = useState([]);
const [institution, setInstitution] = useState();
const dataRecoil = useRecoilValue(contactData);
const handleContact = useCallback(async () => {
const response = dataRecoil.data;
if (response) {
setData(response.filter((contact) => !contact.deleted));
}
}, [setData, dataRecoil]);
useEffect(() => {
handleContact();
}, [handleContact]);
const columns = useMemo(
() => [
{
Header: 'Nome',
accessor: 'name',
},
{
Header: 'Sigla',
accessor: 'abbreviation',
},
{
Header: 'Nome Fantasia',
accessor: 'institution_id',
Cell: async ({ cell }) => {
const response = await getInstitutionById(cell.row.values.institution_id);
const result = [response.data];
const inst = result.map((inst) => {return inst.name});
const institution_name = inst[0];
console.log(institution_name);
return institution_name;
},
},
],
[editToggle, handleDelete],
);
return (
<>
<Table columns={columns} data={data} />
</>
);
};
Which works at the level of fetching the right data, but does not render the page and shows errors:
The error
The right data being shown in the console.log
The expected output would be to show those names on the console.log on place of that long ID of the first picture.
So, is it possible to do what I am trying to do? And if so, what might am I be doing wrong?
I believe the issue is that you are providing an async function for your cell, which will return a Promise, not the institution name as you are expecting.
A potential solution is to instead create a custom Cell component that uses state to store the institution name. I have provided an example below, which was guided by this example, however I have not tested the code at all, so use it as more of a guide.
const MyCell = ({ cell }) => {
const [institutionName, setInstitutionName] = useState('fetching...')
useEffect(() => {
const getInstitutionName = async (id) => {
const response = await getInstitutionById(id);
const result = [response.data];
const inst = result.map((inst) => {return inst.name});
const institution_name = inst[0];
console.log(institution_name);
setInstitutionName(institution_name)
}
getInstitutionName(cell.row.values.institution_id)
}
return institutionName
}
const Contacts = ({ match }) => {
const [data, setData] = useState([]);
const [institution, setInstitution] = useState();
const dataRecoil = useRecoilValue(contactData);
const handleContact = useCallback(async () => {
const response = dataRecoil.data;
if (response) {
setData(response.filter((contact) => !contact.deleted));
}
}, [setData, dataRecoil]);
useEffect(() => {
handleContact();
}, [handleContact]);
const columns = useMemo(
() => [
{
Header: 'Nome',
accessor: 'name',
},
{
Header: 'Sigla',
accessor: 'abbreviation',
},
{
Header: 'Nome Fantasia',
accessor: 'institution_id',
Cell: MyCell
},
],
[editToggle, handleDelete],
);
return (
<>
<Table columns={columns} data={data} />
</>
);
};

Resources