How to setup React Router <Link> inside a Datatables.net Cell? - reactjs

I have a datatables table (https://datatables.net) initialized like this:
$('#example').DataTable({
data: data,
columns: [{
title: "Database ID",
data: 'database_id',
render: function (data, type, row) {
return '<NavLink to="/shop">'+ data +'</NavLink>';
}
}]
});
The NavLink that i have in the code is supposed to render a database cell as a clickable link because of React-Router (This whole function is inside a React component), but the link is not rendering when i run the code.
What i ultimately want is the ability to click on a database cell that will take me to another component by routing into a link like "/shop/id" but i am stuck in it for a long time. Help!

I was facing the same issue and below solution is working for me.
You can directly add anchor tag with href to the path where you want to route upon on the click. But, it will reload your application.
To avoid reloading of the application, try below solution.
When you're initializing the columns dynamically, you can add below code for the column where you want to have a link.
fnCreatedCell: (nTd, data) => ReactDOM.render(
<a
onClick={() => handletableclick(data, props)}>
{data}
</a>, nTd)
Add handletableclick() function/method in your file to handle click event and programmatically through history push the path where you want to route upon the click
function handletableclick(data, props) {
props.history.push("/pathToRoute=" + data);
}
Also,in above code to access history from props, you will need to enclose your component with withRouter
like below :
export default withRouter(YourComponentName);

Related

Docusaurus 2 How to add custom react component in navbar

From the docusaurus docs, the navbar items can only have certain types like link, dropdown and search.
How do I add custom buttons like if I want to add login button ?
This would really depend on the type of functionality you're wanting to see out of the item you add to the navbar, but the development patterns should be similar across various implementations.
If you're trying to trigger something like a login modal when the user clicks your custom button, you could specify the following in docusaurus.config.js:
module.exports = {
themeConfig: {
navbar: {
items: [
{
href: '#login',
label: 'Login'
}
]
}
},
scripts: [
'https://yourdomain.com/customscript.js'
]
};
Then in a script, customscript.js, you could include the following:
document.querySelector('[href="#login"]')
.addEventListener('click', () => {
console.log('Login button clicked.');
});
Docusaurus requires that either href or to is given on each navbar item, so that's why I chose the weird selector, but if you wished, you could also specify className on the item, and then use that as a selector too. If you want the item to be something other than a link, you could set the outerHTML in your custom script or use replaceWith().
Keep in-mind that depending on the way your site's routing is configured, you may need to re-apply the logic in your custom script if the link node is re-written to the DOM by React.
As far as I know, there isn't really a perfect way to accomplish this at the moment, but v2 is also still in development, so the plugin exposures are getting better with each release.
The temporary workaround works well
https://github.com/facebook/docusaurus/issues/7227

Changing Query paramers while staying on the same page without reload= NextJS Router

for a project I am working on I am running into a problem with the nextjs Router.I have a component that has an input field which the user should be able to input their searchterm in. There is a different component which should be able to get this searchterm and perform a search.
Because the two components aren't connected I would like to set the queryParameters in the router in the Input component, and then execute a function in the search component when the searchTerm is changed.
The problem lies in the following: The searchComponent receives the nextJS router as props and will only execute my useEffect function when those props are changed (and react knows they are changed), on top of that I need to stay on the same page when updating the query parameters, but the route of this page is dynamic. For example: the user can add this combination of components on /search but also on /lookforitem.
I have tried setting the queryParameters in the following way in the Input component:
function setQueryParams() {
router.query = {
...router.query,
searchTerm: input.current,
};
}
In combination with the following code in the Search component:
useEffect(() => {
console.log('Router has changed');
}, [router]);
The problem is that this useEffect doesnt get called untill the search component is rendered again (I have created a button that logs the router to the console, and it shows the updated router), which I assume is because React hasn't realised that the Router props have changed.
I have also tried setting the query parameters via a router.push in the following way:
function setQueryParams() {
router.push(
{
pathname: router.route,
query: {
...router.query,
searchTerm: input.current,
},
},
undefined,
{ shallow: true }
);
}
However this comes with its own set of problems. First of all it causes a refresh of the page, which I don't want. On top of that it changes the url to for example: /search?searchTerm=Hello which means that if I enter a different input and submit it will stack making the next url for example: &searchterm=hello?searchterm=goodbye.
I want a way to update the query parameters without refreshing the page, but while also notifying the other components that use the router that the query parameters have updated. All of the searching that I've done seems to be specific to either routing to a different page or routing to a predefined page.
Any help would be greatly appreciated.

React global click track handler

I am new to react and working on a legacy codebase. Am wondering if we can write a global button click handler for click tracking.
The jQuery equivalent of this would be something like,
utilities.js :
var subscribeForClickTracking = function() {
$( "button" ).click((event) => { console.log($(event.target).html()) })
$( "p" ).click((event) => { console.log($(event.target).html()) })
}
In all the html files, will add reference to utiliies.js and this snippet of code.
$(document).ready(function () {
subscribeForClickTracking();
});
I have surfed about this and reached similar so questions, like
Higher Order React Component for Click Tracking
and https://www.freecodecamp.org/news/how-to-track-user-interactions-in-your-react-app-b82f0bc4c7ff/
But these solutions involve modifying the button's implementation, which would lead to huge change. (For a html form with 50+ buttons).
Is there an alternate approach to achieve something similar to the above jQuery approach.
Thanks in advance.
No, you can not do that. The reason is that React prevents you from doing global things to avoid side effects.
I think the proper React way would be to create your own Button component.
First create a new component :
export default Button = (props) => <button ...props />
Then, you can import and use Button instead of button in any component.
Then in your Button component, you can override your onClick method like this :
<button
...props
onClick={() => {
// doWhatYouWantHere;
props.onClick()
/>
However, as React is JavaScript, you can still use vanilla JavaScript to attach an event to every button

How to use onClick event on Link: ReactJS?

In React router, I have to and onClick attributes as shown below
<li key={i}><Link to="/about" onClick={() => props.selectName(name)}>{name}</Link></li>
state = {
selectedName: ''
};
selectName = (name) => {
setTimeout(function(){this.setState({selectedName:name});}.bind(this),1000);
// this.setState({selectedName: name});
}
to attribute navigates to about Route
onClick assigns value to state variable selectedName which will be displayed when navigated to About page.
When I give timeout insided function called on click, its navigating to new page and after sometime state is getting updated resulting in displaying previous name until state is updated with new name.
Is there a way where it will navigate to the new route only after the code in onClick function gets executed.
You can get the entire code [here].(https://github.com/pushkalb123/basic-react-router/blob/master/src/App.js)
One possible way is, Instead of using Link, use history.push to change the route dynamically. To achieve that remove the Link component and define the onClick event on li. Now first perform all the task inside onClick function and at the end use history.push to change the route means to navigate on other page.
In your case change the router inside setState callback function to ensure that it will happen only after state change.
Write it like this:
<li key={i} onClick={() => props.selectName(name)}> {name} </li>
selectName = (name) => {
this.setState({ selectedName:name }, () => {
this.props.history.push('about');
});
}
Check this answers for:
When to use setState callback
How to navigate dynamically using react router dom
Alternatively, I would recommend using URL Params in order to capture the name of the person that the about page is about. Thus, instead of the url being /about and the name being behind the scenes, it would be /about/tom or /about/pushkal. The way that you do this is by defining params in the URL router as such in your index.js:
<Route path="/about/:name" component={AboutPage}>
Now, when you link to the about page, you would do it as such:
<Link to={"/about/" + name}>{name}</Link>
Now, in your AboutPage component, you can access the name param as a prop in this.props.params.name. You can look at more examples here.
This method is a bit different than your current approach but I suspect it will lead to easier design later on

How to prevent route change using react-router

There's a certain page in my React app that I would like to prevent the user from leaving if the form is dirty.
In my react-routes, I am using the onLeave prop like this:
<Route path="dependent" component={DependentDetails} onLeave={checkForm}/>
And my onLeave is:
const checkForm = (nextState, replace, cb) => {
if (form.IsDirty) {
console.log('Leaving so soon?');
// I would like to stay on the same page somehow...
}
};
Is there a way to prevent the new route from firing and keep the user on the same page?
It is too late but according to the React Router Documentation you can use preventing transition with helping of <prompt> component.
<Prompt
when={isBlocking}
message={location =>
`Are you sure you want to go to ${location.pathname}`
}
/>
if isBlocking equal to true it shows a message. for more information you can read the documentation.
I think the recommended approach has changed since Lazarev's answer, since his linked example is no longer currently in the examples folder. Instead, I think you should follow this example by defining:
componentWillMount() {
this.props.router.setRouteLeaveHook(
this.props.route,
this.routerWillLeave
)
},
And then define routerWillLeave to be a function that returns a string which will appear in a confirmation alert.
UPDATE
The previous link is now outdated and unavailable. In newer versions of React Router it appears there is a new component Prompt that can be used to cancel/control navigation. See this example
react-router v6 no longer supports the Prompt component (they say that they hope to add it back once they have an acceptable implementation). However, react-router makes use of the history package which offers the following example for how to block transitions.
Note that to actually make this work in react router you have to replace the createBrowserHistory call with some hackery to make sure you are using the same history object as react router (see bottom of answer).
const history = createBrowserHistory();
let unblock = history.block((tx) => {
// Navigation was blocked! Let's show a confirmation dialog
// so the user can decide if they actually want to navigate
// away and discard changes they've made in the current page.
let url = tx.location.pathname;
if (window.confirm(`Are you sure you want to go to ${url}?`)) {
// Unblock the navigation.
unblock();
// Retry the transition.
tx.retry();
}
You'll need to put this inside the appropriate useEffect hook and build the rest of the functionality that would have otherwise been provided by prompt. Note that this will also produce an (uncustomizable) warning if the user tries to navigate away but closing the tab or refreshing the page indicating that unsaved work may not be saved.
Please read the linked page as there are some drawbacks to using this functionality. Specifically, it adds an event listener to the beforeunload event which makes the page ineligable for the bfcache in firefox (though the code attempts to deregister the handler if the navigation is cancelled I'm not sure this restores salvageable status) I presume it's these issues which caused react-router to disable the Prompt component.
WARING to access history in reactrouter 6 you need to follow something like the instructions here which is a bit of a hack. Initially, I assumed that you could just use createBrowserHistory to access the history object as that code is illustrated in the react router documentation but (a bit confusingly imo) it was intended only to illustrate the idea of what the history does.
We're using React Router V5, and our site needed a custom prompt message to show up, and this medium article helped me understand how that was possible
TLDR: the <Prompt/> component from react-router-dom can accept a function as the message prop, and if that function returns true you'll continue in the navigation, and if false the navigation will be blocked
React-router api provides a Transition object for such cases, you can create a hook in a willTransitionTo lifecycle method of the component, you are using. Something like (code taken from react-router examples on the github):
var Form = React.createClass({
mixins: [ Router.Navigation ],
statics: {
willTransitionFrom: function (transition, element) {
if (element.refs.userInput.getDOMNode().value !== '') {
if (!confirm('You have unsaved information, are you sure you want to leave this page?')) {
transition.abort();
}
}
}
},
handleSubmit: function (event) {
event.preventDefault();
this.refs.userInput.getDOMNode().value = '';
this.transitionTo('/');
},
render: function () {
return (
<div>
<form onSubmit={this.handleSubmit}>
<p>Click the dashboard link with text in the input.</p>
<input type="text" ref="userInput" defaultValue="ohai" />
<button type="submit">Go</button>
</form>
</div>
);
}
});

Resources