Exporting const to another file in React - reactjs

I have a table component that is reusable with different column props. My app uses the component several times, and many times I use the same columns but not all. So I'm trying to export my "const columns = ..." to a separate file. the problem is it can't be a pure function, because I'm using useMemo and numeral... how can I move the my columns to a separate file so they can be accessed by multiple tables or defined separately if needed?
//App.js
import React from 'react'
import styled from 'styled-components'
import { useTable } from 'react-table'
import numeral from "numeral";
import makeData from './makeData'
function Table({ columns, data }) {
// Use the state and functions returned from useTable to build your UI
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
} = useTable({
columns,
data,
})
// Render the UI for your table
return (
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps()}>{column.render('Header')}</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map((row, i) => {
prepareRow(row)
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
})}
</tr>
)
})}
</tbody>
</table>
)
}
function App() {
const columns = React.useMemo(
() => [
{
Header: 'Name',
columns: [
{
Header: 'First Name',
accessor: 'firstName',
},
{
Header: 'Last Name',
accessor: 'lastName',
},
],
},
{
Header: 'Info',
columns: [
{
Header: 'COH',
accessor: 'age',
Cell: (props) => (
<div className="color1">
{numeral(props.value).format("$0,0.000")}
</div>
),
},
{
Header: 'Visits',
accessor: 'visits',
},
{
Header: 'Status',
accessor: 'status',
},
{
Header: 'Profile Progress',
accessor: 'progress',
},
],
},
],
[]
)
const MobileColumns = React.useMemo(
() => [
{
Header: 'Name',
columns: [
{
Header: 'First Name',
accessor: 'firstName',
},
{
Header: 'Last Name',
accessor: 'lastName',
},
],
},
{
Header: 'Info',
columns: [
{
Header: 'COH',
accessor: 'age',
Cell: (props) => (
<div className="color1">
{numeral(props.value).format("$0,0.000")}
</div>
),
},
],
},
],
[]
)
const data = React.useMemo(() => makeData(20), [])
return (
<>
<div className="hide-mobile">
<Table columns={columns} data={data} />
</div>
<div className="show-mobile">
<Table columns={MobileColumns} data={data} />
</div>
</>
)
}
export default App
I attempted to create a new file called Columns.js like so, but I can't use useMemo there it seems.
// Columns.js
import React, { useMemo } from "react";
import numeral from "numeral";
export const columns = React.useMemo(
() => [
{
Header: 'Name',
columns: [
{
Header: 'First Name',
accessor: 'firstName',
},
{
Header: 'Last Name',
accessor: 'lastName',
},
],
},
{
Header: 'Info',
columns: [
{
Header: 'COH',
accessor: 'age',
Cell: (props) => (
<div className="color1">
{numeral(props.value).format("$0,0.000")}
</div>
),
},
{
Header: 'Visits',
accessor: 'visits',
},
{
Header: 'Status',
accessor: 'status',
},
{
Header: 'Profile Progress',
accessor: 'progress',
},
],
},
],
[]
)
export const MobileColumns = React.useMemo(
() => [
{
Header: 'Name',
columns: [
{
Header: 'First Name',
accessor: 'firstName',
},
{
Header: 'Last Name',
accessor: 'lastName',
},
],
},
{
Header: 'Info',
columns: [
{
Header: 'COH',
accessor: 'age',
Cell: (props) => (
<div className="color1">
{numeral(props.value).format("$0,0.000")}
</div>
),
},
],
},
],
[]
)
};

Related

how to add new row or a footer in rc-table in react js

I want to add a new row at the end of rc-table to show total of column value.
const columns = [
{
title: "Name",
className: "cursor-pointer",
dataIndex: "name",
key: "name",
Footer: "Total",
align: alignLeft,
// onHeaderCell: () => onHeaderClick('name'),
render: (name: any) => <span className="whitespace-nowrap">{name}</span>,
},
];
try this:
const columns = [
{
title: "Name",
className: "cursor-pointer",
dataIndex: "name",
key: "name",
align: alignLeft,
render: (name: any) => <span className="whitespace-nowrap">{name}</span>,
footer: (data: any) => {
let total = 0;
data.forEach((item: any) => {
total += item.name;
});
return <div>{`Total: ${total}`}</div>;
},
},
];

Custom handler search filter table antd

I am using antd to create a table with filter feature,
but I got a problem, I want to custom filter search of antd to call api,
antd table
How can I do that with react and antd,
Here is my code
const columns: ColumnsType<Product> = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
render: (name, record) => (
<div className="flex items-center">
<Avatar src={record.avatar}>{name[0].toUpperCase()}</Avatar>
<span style={{ marginLeft: 10 }} className="whitespace-nowrap">
<a title="View & Edit" href={'/product/' + record.id}>
{name}
</a>
</span>
</div>
),
},
{
title: 'About',
dataIndex: 'about',
key: 'about',
render: (about: string) => <p className="text-ellipsis line-clamp-2">{about}</p>,
width: 400,
},
{
title: 'Categories',
dataIndex: 'categories',
key: 'categories',
filterSearch: true,
filters: categories!.map((category: Category) => ({ text: category.name, value: category.id })),
render: (categories: any) => {
console.log(categories);
return '-';
},
// width: 400,
},
{ title: 'Addresses', dataIndex: 'contract_addresses', key: 'address', render: addresses => addresses?.[0] },
];
To filter the table, I usually provide a function that filters the data before it is passed to the table (In my example the function getData. This function can also be used to adjust the filter accordingly. Here is an example, which also consists of a Search-Input to modify the search:
const CustomersTable = (props: any) => {
const [searchText, setSearchText] = useState<string | undefined>(undefined);
const [customers, setCustomers] = useState<ICustomer[]>([]);
const getData = (
sourceData: Array<ICustomer>,
searchText: string | undefined
) => {
if (!searchText) {
return sourceData;
}
return sourceData.filter((item) => {
const comparisonString = `${item.firstname.toLowerCase()}${item.familyname.toLowerCase()}`;
//here you can provide a more sophisticared search
return comparisonString.includes(searchText.toLowerCase());
});
};
const columns = [
{
title: "Vorname",
dataIndex: "firstname",
key: "firstname",
sorter: (a: ICustomer, b: ICustomer) => {
if (a.firstname.toLowerCase() > b.firstname.toLowerCase()) {
return -1;
} else if (a.firstname.toLowerCase() < b.firstname.toLowerCase()) {
return 1;
} else {
return 0;
}
},
},
{
title: "Nachname",
dataIndex: "familyname",
key: "familyname",
},
];
return (
<>
<Search
placeholder="Vorname, Nachname"
value={searchText}
onChange={(e) => {
setSearchText(e.target.value);
}}
/>
<Table
columns={columns}
dataSource={getData(customers, searchText)}
/>
</>
);
};

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?

Adding multiple data to a column in react-table

I have a table using react-table but for one of the columns I want to show two pieces of data - name and description.
getInitialState(){
return {
data: [{
id: 1,
keyword: 'Example Keyword',
product: [
name: 'Red Shoe',
description: 'This is a red shoe.'
]
},{
id: 2,
keyword: 'Second Example Keyword',
product: [
name: 'blue shirt',
description: 'This is a blue shirt.'
]
}]
}
},
render(){
const { data } = this.state;
return (
<div className="app-body">
<ReactTable
data={data}
columns={[{
columns: [{
Header: 'Id',
accessor: id,
show: false
}, {
Header: 'Keyword',
accessor: 'keyword'
}, {
Header: 'Product',
accessor: 'product' // <<< here
}]
}]}
defaultPageSize={10}
className="-highlight"
/>
</div>
)
}
Where the accessor is Product I want to show both the name and description (I'll style them to stack with different font sizes) in the Product column.
I've tried using the Cell: row => attribute for that column and thought I could also try calling a function that lays it out, but I've gotten errors both times.
Any ideas how to do this?
Indeed you should use Cell for this like this:
getInitialState(){
return {
data: [
{
id: 1,
keyword: 'Example Keyword',
product: [
name: 'Red Shoe',
description: 'This is a red shoe.'
]
},{
id: 2,
keyword: 'Second Example Keyword',
product: [
name: 'blue shirt',
description: 'This is a blue shirt.'
]
}]
}
},
render(){
const { data } = this.state;
return (
<div className="app-body">
<ReactTable
data={data}
columns={[{
columns: [{
Header: 'Id',
accessor: id,
show: false
}, {
Header: 'Keyword',
accessor: 'keyword'
}, {
Header: 'Product',
accessor: 'product',
Cell: ({row}) => { //spread the props
return (
<div>
<span className="class-for-name">{row.product.name}</span>
<span className="class-for-description">{row.product.description}</span>
</div>
)
}
}]
}]}
defaultPageSize={10}
className="-highlight"
/>
</div>
)
}
Another thing I spotted was that product property should be an object not an array, so change this:
product: [
name: 'blue shirt',
description: 'This is a blue shirt.'
]
to this:
product: {
name: 'blue shirt',
description: 'This is a blue shirt.'
}
The accepted answer didn't work for me. Here's how I did it:
const [data, setData] = React.useState([
{
name: 'My item',
desc: 'This is a nice item',
},
]);
const columns = React.useMemo(() => [
{
Header: 'Name',
accessor: 'name',
Cell: (props) => (
<>
<p className="item title">{props.row.original.name}</p>
<p className="item desc">{props.row.original.desc}</p>
</>
),
},
]);

react-table iterating over object array to print values in a column

I am using react-table to generate tables(https://react-table.js.org/#/story/readme). I have a state defined as following:
this.state = {
sampleTable:[
{author: 'Mac', books: [{title:'One', price: 20}, {title:'Two', price: 20}]},
{author: 'Rick', books: [{title:'Three', price: 20}, {title:'Four', price: 20}]}
],
sampleTableColumns:[
{Header: 'Author', accessor: 'author'},
{Header: 'Books', accessor: 'books.title'},
],
};
And I am trying to make table as following:
<ReactTable
className="-highlight"
data={this.state.sampleTable}
columns={this.state.sampleTableColumns}
defaultPageSize={10}
/>
However in the books column I see nothing. I am not sure how am I supposed to iterate over books array so that I can print say book titles in books column?
I had to write my accessor like following:
sampleTableColumns:[
{
Header: 'Author',
accessor: 'author',
},
{
Header: 'Books',
id:'books',
accessor: data => {
let output = [];
_.map(data.books, book => {
output.push(book.title);
});
return output.join(', ');
},
},
],
It's simple without using any dependencies like lodash...
You just need to use React-table Cell attribute
sampleTableColumns:[
{
Header: 'Author',
accessor: 'author',
},
{
Header: 'Books',
accessor: 'books',
Cell: (props) => {
const { sampleTable} = props.original;
return (
{ sampleTable.books.map( (book) =>(<h4>{book.title}</h4>)) }
);
},
},],
I believe Shivam Modi answered it. Using TypeScript his solution could be rendered something like (using built-in row selector):
{
Header: "Books",
accessor: "books",
Cell: ({ row }) => {
return (
row.original.books
.map((book: Book) => (
<div key={book.id}>
<h4>{book.name}</h4>
</div>
))
);
},
},
Your books data is an array and I don't believe react-table knows how to render those by default. You can instead supply your own render function in the sampleTableColumns for that cell, which would look something like:
sampleTableColumns:[
{Header: 'Author', accessor: 'author'},
{
Header: 'Books',
accessor: 'books'
render: (rowInfo) => {
return (
<span>
{rowInfo.value.map(book => (<span>{book.title}</span>))}
</span>
},
},
],
Well, I am too late for the party but all above doesn't work for me and I tried to use ES6 syntax. I have requested data in array, so here we go (I renamed variables):
export const returnPreparedField = (array) => {
const newArray = [];
for (let arrayValue of array) {
const newElement = {
id: "element",
header: element,
accessor: data => data.element.find(elementToFind => (elementToFind.name === element))?.value,
show: true,
width: 100
};
newArray.push(newElement);
}
return newArray;
};

Resources