React redirect on click of svg element - reactjs

I have a single page React App that is d3 and SVG heavy, and I would like to be able to redirect from one page to another when a user clicks on an svg rect on one of my pages. I am familiar with this.props.history.push() as well as the <Link> component from the react-router-dom library, however neither of these seem to help in this instance.
The svg element of relevance here is deep in a graphing component of mine that is 3-4 children down from the front-end's main App.js file that does all of the routing, and when I run console.log(this.props) in my component with the svg, there is no history object on the props. I'm not sure if a reproducible example is needed here, as I just need direction.
In short, I have no idea what should go into the on-click function that is associated with my svg rect, to enable redirect in my app. Any thoughts on this would be greatly appreciated!
Edit: obviously this is wrong but i tried to return a Redirect component in on-click handler and it didn't work:
...
...
function handleMouseClick() {
console.log('clicked')
return <Redirect to='/stats' />;
}
myRect.on('click', handleMouseClick)
...
Edit2: should i put the rect elements inside of components in the svg? is that even possible?

You can add the history prop from react-router to a component by wrapping it with withRouter. Just make sure whatever is mounting your component is using the wrapped version (usually by only exporting the wrapped component).
import React from 'react';
import { withRouter } from 'react-router';
class MyComponent extends React.Component {
render() {
return (
<button onClick={() => this.props.history.push('/newpage')}>
Click me
</button>
);
}
}
export default withRouter(MyComponent);

Related

How to wrap ALL components of react in a HOC?

ReactJS is a great library, However, it misses some features which I found in Vue and Angular. These features can be implemented of course in React, however, they require extra code to be written.
Every react component, or every JSX element I should say has the following properties shared, which are given by React to us to consume:
ref
key
I wanted to add extra props:
renderIf
fallback
These props help in a way I can't describe when it comes to conditional rendering and filtering the views based on the logged-in user permissions and roles (and other conditional rendering use cases, of course).
In react, if we wanted to apply these props to our components, we would use a HOC as follows:
// 🍎 Disclaimer: you don't have to understand any of the code written bellow, the general idea is that this is a HOC.
import React from 'react'
import getVal from './getVal'
export default function EnhancedComponent(OriginalComponent) {
return ({ renderIf: renderIf_ = true, override: override_, fallback: fallback_ = undefined, ...props }) => {
const renderIf = getVal(renderIf_)
const override = getVal(override_)
const fallback = getVal(fallback_)
const consumersComponent = <OriginalComponent {...props} />
let render = fallback
if (renderIf) render = consumersComponent
if (override_ !== undefined) render = override
return render
}
}
Where every time you want to apply these props to your components, you would have to wrap every new component you create with EnhancedComponent as follows:
export default EnhancedComponent(function Sidenav(){
return <div> side nav </div>
})
Now, you can use your Sidenav component within your App component as follows:
import Sidenav from './Sidenav'
export default function App(){
return (
<div>
<Sidenav renderIf={(5 + 5 === 10)}/>
<div>etc</div>
</div>
)
}
This API is great, but it has a drawback, which is, every time you want to apply these cool props (renderIf and fallback) you'll have to repeat these steps:
import Enhanced component to your file.
wrap your export with Enhanced component.
What I am looking for, is a method, or a way to inherit, or to add some props to the original react component class, somehow?
In react class components, I can imagine doing this on the React.Component class which we used to extend from in the past
class Car extends React.Component{
constructor(){}
render(){
return <div>I miss you 🌹</div>
}
}
But in react functional component, how can we do that?
I want to apply these props by default everytime I create a new component, without wrapping my components in a HOC everytime.
Does React have a way to do that? To change its defaults ?

How to use react-router inside of a method

I'm new to react and react router dom(and javascript in general), and I've been racking my brain and googling all over the place but I can't figure out how to redirect someone in a method like exampleMethod() {}. I then call said method in an onClick function on a button. Here's my code for the button:
<Button color="secondary" onClick={this.rejoinPrevRoom}>
Rejoin previous room
</Button>
The button is inside a method called renderHomepage which is then called if there is a room code in the state inside the main render function.
The code for rejoinPrevRoom:
rejoinPrevRoom() {
console.log("Debug: A");
console.log(this.state.roomCode);
return <Redirect to={`/room/${this.state.roomCode}`} />;
}
It successfully logs a code so there is one but I don't know why it doesn't redirect them.
Any help would be appreciated greatly
Thank you,
Paddy
You cannot return JSX from a callback handler and expect it to have any effect on what the component is returning to render to the DOM. In this case you can either set some state to conditionally render a Redirect component as part of the render return, or use the history object to issue an imperative redirect via history.replace (not history.push).
rejoinPrevRoom() {
console.log("Debug: A");
console.log(this.state.roomCode);
this.props.history.replace(`/room/${this.state.roomCode}`);
}
If this component is not rendered directly by a Route component via the component, render, or children prop, then you can decorate it with the withRouter Higher Order Component in order to have the route props injected as props and have the history object available as this.props.history.
Update
In order to inject the route props change your HomePage export from
export default class HomePage extends Component {
....
}
to
class HomePage extends Component {
....
}
default export withRouter(HomePage);
If you're using React hooks you can redirect like the following:
const history = useHistory();
rejoinPrevRoom() {
console.log("Debug: A");
console.log(this.state.roomCode);
history.push(`/room/${this.state.roomCode}`);
}
If you're using class components:
rejoinPrevRoom() {
console.log("Debug: A");
console.log(this.state.roomCode);
const {history} = this.props;
history.push(`/room/${this.state.roomCode}`);
}
If the component is inside a BrowserRouter, you can use history to do that.
rejoinPrevRoom() {
console.log("Debug: A");
console.log(this.state.roomCode);
this.props.history.replace(`/room/${this.state.roomCode}`);
}

React dynamically create routes

First things first is this possible to have 2 components on a page one displaying simple static markup but the second one in my parent is going to be a div that displays a link for each item in an array and if you click on it at the bottom of the div then data will be displayed for each one?
If this is possible are dynamic props as simple as performing .map inside the element and printing out a route?
Will also add that I am receiving the following on my Router object but I installed react-router globally though it is also in my node_modules folder.
'react-router' does not contain an export named 'hashHistory'.
import React, { Component } from 'react';
import { Router, Route, IndexRoute, hashHistory } from "react-router";
class DetailsComponent extends Component {
render() {
return (
<Router history={hashHistory}>
// in here will map this.props.data and for each one print a route
// the component for that route will be a DetailedViewComponent that
// that takes in the data in the props and renders it
// so i might need a link? in this render method aswell?
</Router>
);
}
}
export default DetailsComponent ;
React Router match params can be used to render content dynamically
ie. <Route path="/dynamicroute/:id" component={DynamicComponent}/> will provide an id param that can be retrieved via props.match.params inside <DynamicComponent/>
Spoke to some more senior react developers and they said what I was after was npm package Component-Router I haven't got back onto the project just yet but will post full code once completed :)

Can we have React 16 Portal functionality React Native?

I'm using React Native which ships with React 16 alpha release which supports portals. While in browser and having access to DOM we can use id or classes to access element from anywhere in component/file hierarchy like this:
const modalRoot = document.getElementById('modal-root');
and pass it to createPortal(child, container) container arg. React docs clearly says than container should be DOM element:
The second argument (container) is a DOM element.
This function is also a method of ReactDOM which doesn't exist in React Native.
Is there a way to achieve the similar functionality in React Native?
Use case:
I want to render an animated overlay in the root of my application but pass the Animated values props to it from a parent deep in the tree hierarchy (can't use Redux actions for such things).
I had similar problem where I wanted to render overlay on top of everything from deeply nested child component. I solved my problem with React Native's Modal
It renders its content on top of everything :) Easy to use and no need for extra dependencies
I don't think react-native provides this functionality in its own API.
But there is a library available which provides the similar functionality. react-gateway
As per the docs of react-gateway,
It also works in universal (isomorphic) React applications without any additional setup and in React Native applications.
React Gateway does not directly depend on react-dom, so it works fine with React Native under one condition:
You must pass React Native component like View or similar to component prop of .
import React from 'react';
import { Text, View } from 'react-native';
import {
Gateway,
GatewayDest,
GatewayProvider
} from 'react-gateway';
export default class Application extends React.Component {
render() {
return (
<GatewayProvider>
<View>
<Text>React Gateway Native Example</Text>
<View>
<Gateway into="one">
<Text>Text rendered elsewhere</Text>
</Gateway>
</View>
<GatewayDest name="one" component={View} />
</View>
</GatewayProvider>
);
}
}
The above example is taken from the repo itself. react native example
One way to render the items above the screen can be done using react-native-paper library.
import * as React from 'react';
import { Text } from 'react-native';
import { Portal } from 'react-native-paper';
const MyComponent = () => (
<Portal.Host>
<Text>Content of the app</Text>
</Portal.Host>
);
export default MyComponent;
Portal host renders all of its children Portal elements. For example, you can wrap a screen in Portal.Host to render items above the screen.
Here is the link which describes its usage:
https://callstack.github.io/react-native-paper/portal-host.html

this.props.history.push not working

When i export a component like:
export default class Link extends React.Component{
onLogout() {
this.props.history.push('/');
}
the button that this is tied to correctly changes the page in react-router v4.
However, in my main.js file, I am currently trying to get something like:
if (insert conditional here) {
this.props.history.push('/');
}
to work. But it just give me a type error.
'Uncaught TypeError: Cannot read property 'history' of undefined'.
I do have all the correct dependencies installed, and it works just fine in my other component. I'm currently having this if statement in the main js file (its a small project im practicing to understand v4), so I'm thinking it might be because I'm not extending a class.
Does anyone have any idea why the same code wouldn't be working in the main file, and is there a workaround for this? All the react-router v4 changes are befuddling this rookie.
This means that this.props is not defined, because you are using this.props in a callback where this is not what you think it is. To solve this, use your callback like this:
<button onClick={this.onLogout.bind(this)}>
instead of
<button onClick={this.onLogout}>
You can also do
import { browserHistory } from 'react-router'
... browserHistory.push('/');
Edit: Regarding the latter, you can also wrap your component with this:
import { withRouter } from 'react-router';
// in YourComponent:
... this.props.router.push('/');
export default withRouter(YourComponent);
It may be better than browserHistory because you don't have to specify the history type (could be changed to hashHistory and still work).
I would suggest you to not a address directly into the history but use .
You can send the user to the page and you can conditionally check if he is allowed to see the content if yes then render in render method return the component else return
This would result in a cleaner code.
https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/Redirect.md

Resources