Navigating Using React Router From a Button Outside the React App - reactjs

I have a simple demo react app that uses react-router-dom (5.2) to show one of 3 "pages".
The app is included on a page that has a button:
index.html:
<button data-app-button data-sku='woo-beanie'>Click Me</button>
index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
document.addEventListener('click', function (event) {
if (event.target.closest('button[data-app-button]')) {
// send instructions to react
}
});
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
I want to be able to navigate to a page in the react site, passing through the buttons data-attributes. How is this done with react and react-router ?
UPDATE
#Doppoio's solution works - as long as I'm on a different "page" in my react app. However I have a route like this:
<Route
path="/tryon/:id/:product_sku?">
</Route>
If I start in app from a route of say /faqs and my external button navigates to /tryon/242/jumper-23 my component is awar of the product_sku property.
However when I'm on a page in app of /tryon/242 and then i click an external button to navigate to /tryon/242/jumper-23 the component should be aware of the jumper-23 optional parameter. Currently it isn't.
How do i make the Tryon component detect the change in url of just the optional parameter?

Somewhere in your code under Router, you can add history to window object. And call it from there.
const SomeComponentInsideRouter = () => {
const history = useHistory();
useEffect(() => {
window.reactHistory = history; // Add reference to history via window object.
}, []);
return null;
};
And call it via window.reactHistory
document.addEventListener("click", function (event) {
if (event.target.closest("button[data-app-button]")) {
// send instructions to react
window.reactHistory.push("/about");
}
});
Here's sandbox link
https://codesandbox.io/s/happy-ganguly-b0u2o?file=/src/index.js
Update to mention changed props:
Changes to the props can be detected using componentDidUpdate
https://reactjs.org/docs/react-component.html#componentdidupdate

Related

Performing a condition based on the current URL with react.js and react router v6

Using React.js & react router v6
Is there a way I can extract information from the current URL? Let's say my current URL is this: localhost:3000/about/x567gfh67ssd90g
I simply want to perform a condition based on the current URL. Is there any possible way of doing this?
import { useLocation } from "react-router-dom";
const Component = () => {
const location = useLocation();
useEffect(() => {
console.log(location);
});
return ();
};
You can get pathname using useLocation.
You can also add connected-react-router to your project. This way you will have access to all the current route information in your redux-store. This will also allow you to "Time travel" through your SPA. When you hit the backbutton instead of going to the previous page you can go back through state.
import React from 'react';
import { render } from 'react-dom';
import { Provider, ReactReduxContext } from 'react-redux';
import { ConnectedRouter } from "connected-react-router";
import { store, history } from './Store/store';
import { App } from './App/App';
render(
<Provider store={ store } context={ ReactReduxContext } >
<ConnectedRouter context={ ReactReduxContext } history={ history }>
<App />
</ConnectedRouter>
</Provider>,
document.getElementById('app')
);
If you install the redux-devTools in your browser you can look at all the URL data like this- Obviously this is a little more advanced but you it will give you access to everything you need.

Is there any way to block user navigation with out any prompt in react

We have implemented our application navigation using react-router.
Is there any way to block user navigation(including forward,backward buttons) with out any prompt in react.
react-router history block subscribe function i can't use as it will throw default prompt. In my case i want to show my custom dialog.
You can add an event listener popState to freeze the navigation.
Working demo
const Home = ({ history }) => {
useEffect(() => {
const browserNavigationHandle = () =>
window.history.pushState(null, null, document.URL);
window.history.pushState(null, null, document.URL);
window.addEventListener("popstate", browserNavigationHandle);
return () =>
window.removeEventListener("popstate", browserNavigationHandle);
}, []);
return <div>Home</div>;
};
export default Home;
There is no way to block a users browser navigation, as that would take away all control of a user and could be used in a very malicious way. What you can do though is stop your application from changing.
One way could be to listen to history changes and navigate back to the page the user should be on. Problem with that is will re-render and you might lose states, depending on your application.
Another way could be (as I havent tried this before) to use a customized history object. Check out how to setup the Router with history. As seen in their example:
import React from "react";
import ReactDOM from "react-dom";
import { Router } from "react-router";
import { createBrowserHistory } from "history";
const history = createBrowserHistory();
ReactDOM.render(
<Router history={history}>
<App />
</Router>,
node
);
There might be a way to customize that history object so you can control its change and the applications response.
To disable back button you can do below code in higher order component
componentDidMount() {
const { history } = this.props;
window.addEventListener("popstate", () => {
history.go(1);
});
}
but this component must be a Route component
Just add useHistory().go(1) working for me.
use this to prevent routing
window.addEventListener('beforeunload', (event) => {
event.preventDefault();
event.stopPropagation();}

How is react-router being created when using contextTypes?

I've got react-router 4.0.0-alpha.4 installed in my React project, which is a tutorial that I'm following, and I'm having a really difficult time understanding how it's being called in StorePicker.jsx
App.jsx
import React from 'react';
import { render } from 'react-dom';
import './css/style.css';
import {BrowserRouter, Match, Miss} from 'react-router';
import StorePicker from './components/StorePicker';
import App from './components/App';
import NotFound from './components/NotFound';
const Root = () => {
return(
<BrowserRouter>
<div>
<Match exactly pattern="/" component={StorePicker}/>
<Match pattern="/store/:storeId" component={App}/>
<Miss component={NotFound}/>
</div>
</BrowserRouter>
)
}
render(<Root/>, document.querySelector('#main'));
So towards the end in StorePicker.jsx I create a contextTypes property on the StorePicker object. I then set the value equal to { router: React.PropTypes.object }. React.PropTypes.object has nothing to do with the router though.
But in goToStore(event) all of a sudden the router object is available in this.context.router.
Where did this come from? Is it because the word router is a special keyword when I use it in contextTypes, and React.PropTypes.object somehow knows to fetch the router object as a result and add it to this.context?
Why is this tutorial even telling me to use this pattern? According to the React docs, context should be avoided: https://facebook.github.io/react/docs/context.html
Is there a better way to do this?
StorePicker.jsx
import React from 'react';
import { getFunName } from '../helpers.js'
class StorePicker extends React.Component{
goToStore(event){
event.preventDefault();
const storeId = "1234";
this.context.router.transitionTo(`/store/${storeId}`);
}
render(){
return (
<button onClick={(e) => this.goToStore(e)}>CLICK ME</button>
)
}
};
StorePicker.contextTypes = {
router: React.PropTypes.object
}
export default StorePicker;
The code from this tutorial works, but I have no idea why it's working.
3.Is there a better way to do this? :
You should not use context. It seems you only require to redirect to an url (whose value depends on the text box) when the form submits.
In React, you can use react routers.
Here's some more examples: StackOverflow: programmatically-navigate-using-react-router
Or in Javascript, if you just have to redirect to an url location you can simply use 'window.location' function as follows:
goToStore(event) {
event.preventDefault();
const storeId = this.storeInput.value;
window.location.assign(`/store/${storeId}`)
}
However, you may not want to use second option as Javascript renders whole dom and then there's no advantage of using React then.

Link to changes state but does not change URL

Using renderToString to render my components server-side. All of that is working just fine. If I manually enter a URL like /register, my components are rendering perfectly.
Problem is, when using <Link> in my app, the state is changing but my URL is not updating at all.
route.js:
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { browserHistory, Router, match } from 'react-router';
import routes from './routes';
import store from './stores';
// Server rendering works identically when using async routes.
// However, the client-side rendering needs to be a little different
// to make sure all of the async behavior has been resolved before the
// initial render,to avoid a mismatch between the server rendered and client rendered markup.
match({ location:browserHistory, routes }, (error, redirectLocation, renderProps) => {
render((
<Provider store={store}>
<Router {...renderProps} />
</Provider>
), document.getElementById('root'));
});
I have a feeling it could be due to browserHistory, is there something I'm missing?
I was giving match() the wrong arguments. Instead of:
match({ location:browserHistory, routes }
It should have been:
match({ history:browserHistory, routes }

Automatic redirect after login with react-router

I wanted to build a Facebook login into my react/react-router/flux application.
I have a listener registered on the login event and would like to redirect the user to '/dashboard' if they are logged in. How can I do that? location.push didn't work very well, except after reloading the page completely.
React Router v3
This is what I do
var Router = require('react-router');
Router.browserHistory.push('/somepath');
React Router v4
Now we can use the <Redirect>component in React Router v4.
Rendering a <Redirect> will navigate to a new location. The new location will override the current location in the history stack, like server-side redirects.
import React, { Component } from 'react';
import { Redirect } from 'react-router';
export default class LoginComponent extends Component {
render(){
if(this.state.isLoggedIn === true){
return (<Redirect to="/your/redirect/page" />);
}else{
return (<div>Login Please</div>);
}
}
}
Documentation https://reacttraining.com/react-router/web/api/Redirect
React Router v0.13
The Router instance returned from Router.create can be passed around (or, if inside a React component, you can get it from the context object), and contains methods like transitionTo that you can use to transition to a new route.
React Router v2
Even though the question is already answered, I think it's relevant to post the solution that worked for me, since it wasn't covered in any of the solutions given here.
First, I'm using the router context on my LoginForm component
LoginForm.contextTypes = {
router: React.PropTypes.object
};
After that, I can access the router object inside my LoginForm component
handleLogin() {
this.context.router.push('/anotherroute');
}
PS: working on React-router version 2.6.0
React Router v3
Navigating Outside of Components
create your app with Router like this
// Your main file that renders a <Router>:
import { Router, browserHistory } from 'react-router'
import routes from './app/routes'
render(
<Router history={browserHistory} routes={routes} />,
mountNode
)
Somewhere like a Redux middleware or Flux action:
import { browserHistory } from 'react-router'
// Go to /some/path.
browserHistory.push('/some/path')
// Go back to previous location.
browserHistory.goBack()
react-router/tree/v3/docs
React Router v4.2.0
I am using React-16.2.0 & React-router-4.2.0
And I get solution by this code
this.props.history.push("/");
My working code:
.then(response => response.json())
.then(data => {
if(data.status == 200){
this.props.history.push("/");
console.log('Successfully Login');
}
})
I was following this document redirect-on-login-and-logout
I was also try by return <Redirect to='/' /> But unlucky, this not working for me.
React router v5 using hooks
These steps are for authorisation redirect. But can be used for login/logout redirection also.
The <Redirect/> accepts to prop as a string or an object. We can utilise the object to pass the redirection path after login/logout using hooks easily.
Get the pathname of url from where the <Redirect/> is called using
useLocation()
const {pathname} = useLocation()
In the to prop of <Redirect/> pass in the following object:
<Redirect to={{pathname:'/login',state: {referrer: pathname}}/>
In the Login component access the route state variable using useLocation() hook and use the useHistory() hook to redirect after successful login.
const history = useHistory();
const location = useLocation();
const login() => {
// After login success
const {state: {referrer}} = location;
history.push(referrer)
};
Check the official docs here
React Router v3
Navigating inside components
You should use withRouter decorator when it's necessary to redirect inside a component. The decorator uses context instead of you.
import {withRouter} from 'react-router'
fucntion Foo(props) {
props.router.push('/users/16');
}
export default withRouter(Foo);
withRouter(Component, [options])
A HoC (higher-order component) that wraps another component to enhance
its props with router props.
withRouterProps = {
...componentProps,
router,
params,
location,
routes
}
Pass in your component and it will return the
wrapped component.
You can explicit specify router as a prop to the wrapper component to
override the router object from context.
In your store:
data.router.transitionTo('user');
And router has:
"Route name="user" handler={User}"
User is route handler

Resources