react-tabulator
react-tabulator copied to clipboard
How can I create a custom link formatter?
I cannot for the life of me get the formatter to work.
I get blank cells instead of a formatted link.
The React documentation could really use some explanations.
Side-question: Can I use the vanilla Tabulator in React, instead of the adapted one?
import React, { useState, useEffect } from "react";
import { Link } from 'react-router-dom';
import "tabulator-tables/dist/css/tabulator.min.css";
import { ReactTabulator } from 'react-tabulator'
function Journals(props) {
const [journals, setJournals] = useState([]);
const [inputs, setInputs] = useState([]);
useEffect(() => {
fetch("http://localhost:4000/journals")
.then(res => res.json())
.then(data => {
setJournals(data)
})
.catch(err => err);
}, []);
const journalLinkFormatter = (cell, formatterParams) => {
let key = cell.getValue()
let link = `/journals/${key}`
return <Link style={{ color: "blue", fontWeight: "bold", background: "red" }} to={link}>{key}</Link>
}
const columns = [
{ title: "Number", field: "key_", formatter: journalLinkFormatter },
{ title: "Date", field: "date_" },
];
return (
<div>
<h1>Journals</h1>
<ReactTabulator
data={journals}
columns={columns}
tooltips={true}
layout={"fitData"}
/>
</div >
)
}
export default Journals;
This way works, but it beats the purpose, as it reloads everything.
const columns = [
{
title: "Number",
field: "key_",
formatter: "link",
formatterParams: { url: cell => { return "/journals/" + cell.getValue() } }
},
{ title: "Date", field: "date_" },
];
I'd like to learn how to use custom formatters.
@8483 have you tried this example? https://github.com/ngduc/react-tabulator/blob/master/src/ReactTabulatorExample.tsx#L22
How do I make your example work in my case. I really have no idea.
I am getting a Link outside a Router
error, for no reason. Any advice?
https://stackoverflow.com/questions/58645310/link-outside-a-router-error-while-everything-set-up-properly
Here's a CodeSandbox that demonstrates the problem.
I got this reply on StackOverflow.
react-tabulator reFormatter
is incompatible with react-router library.
https://github.com/ngduc/react-tabulator/blob/0.10.3/lib/Utils.js#L30
From source code,
function reactFormatter(JSX) {
return function customFormatter(cell, formatterParams, onRendered) {
//cell - the cell component
//formatterParams - parameters set for the column
//onRendered - function to call when the formatter has been rendered
onRendered(function () {
var cellEl = cell.getElement();
var CompWithMoreProps = React.cloneElement(JSX, { cell: cell });
react_dom_1.render(CompWithMoreProps, cellEl.querySelector('.formatterCell'));
});
return '<div class="formatterCell"></div>';
};
}
rendering of a formatted element uses the ReactDOM.render
function to render the formatted element directly to DOM isolated from parent elements.
A fix to react-tabulator needs to be done to support this use case. One way to go is to have customFormatter
return a custom component that provides a way to set its state from outside it. Then onRendered
can call this function to set cell
.
@8483 I will look into this to see any alternative to ReactDOM.render If anyone can help, it'll be great.
Thank you for considering it! It really is a must have.
Can we use portals for this instead?
As of right now anytime you interact with the Table in a way that triggers reload (sorting by column for instance) it continually instantiates and appends a completely unique React instance to the DOM. Sort two or three times and all of a sudden your 10 rows with 2-3 custom ReactFormatted cells are now hundreds of mounted and abandoned React DOM/rendered instances.
@8483 @ericnkatz I fixed the reactFormatter so it can re-render on cell edit, new version 0.12.0.
will look into react dom & react-router.
Cf. this react doc, shouldn't there be an "opposite" action of ReactDOM.render()
? I.e. call ReactDOM.unmountComponentAtNode()
when the cell is disposed, so that some cleanup is done?
It's normal that Link
doesn't work. The Router
instance from the main react tree needs to be somehow stored as a global plain JS var and passed to tabulator. And then, within the custom renderer, provided via Context
. This way, Link
retrieves the Router
instance.
I have created an implementation using portals in this gist. It's a Storybook pages. I'm using semantic-ui-react
in my project, so a copy/paste of the file may need adapting.
I don't know if it's OK to have thousands of portals. I.e. one per cell. Also, I didn't see any action do "dispose" a portal. I hope that disposing the DOM element where the portal was rendered suffices. Otherwise => memory leaks possible I think.
Easy way is conditional rendering all page with tabulator So, this way warked for me:
const [redirect, setRedirect] = useState({ allow: false })
const columns = [
{ title: 'No', field: 'no', },
{
title: 'Patient', field: 'patient', cellClick: function (e, cell) {
const rowInfo = cell._cell.row.data
setRedirect({ allow: true, id: rowInfo.id }) //here we update state
}
},
]
//....
if (redirect.allow) {
return <Redirect to={{
pathname: '/accounts/profile',
state: {
id: redirect?.id
}
}} />
} else {
//...
}