Server side redirect with ReactJs - reactjs

I am trying to issue a server side redirect for certain pages for SEO reasons. The problem I am trying to solve is that some of my pages will get index by google, so if they are deleted or taken offline we want to redirect them to a different page rather than showing 404. What is the best way to achieve such redirects with React Router on the server side(express.js)?

You should have a component which should define routing rules, something like this:
<Router>
<Switch>
<Route path='/login' component= {LoginPage}/>
......
</Switch>
<Router>
All I am saying is that you should define a wrapper which define the redirect url:
var redirectToUrl = get url from your logic here;
var urlComponent = '';
if ( redirectToUrl )
{
urlComponent = <Redirect to={listing}/>;
}
Now add this to your url handling page:
<Router>
<div>
{urlComponent}
<Switch>
<Route path='/login' component= {LoginPage}/>
......
</Switch>
</div>
<Router>

const redirect = (a,replace) => {
if(a.params.splat == "page1")
replace("/page2")
}
<Route path="*" ... onEnter={redirect}/>
in server.js
match(...,(error, redirectLocation, renderProps) => {
... else if (redirectLocation) { res.redirect(302,`${redirectLocation.pathname}${redirectLocation.search}`...);

If you are using React Router 4, tou can add Redirect component inside your Page component to redirect pages that are dead. Example: Let's say that component tried to fetch object from API but got an offline response and stored it in component state:
import React, { Component } from 'react';
import { Redirect } from 'react-router-dom';
export default class PageComponent extends Component {
// ... other stuff
render() {
if (this.state.page_offline) {
return (<Redirect to="/some-page-for-offline-data" />);
}
return (...); // normal page render
}
}

Related

React router conditional routes not working

I have a couple of routes I want to show only when a certain condition is met. If this condition is met, and the routes are enabled navigating to them through the URL is not possible, and the Redirect always gets hit
constructor(props: {}) {
super(props);
this.state = {
configuration: new Configuration({}),
}
async function GetConfiguration() {
try {
var response = await fetch("/getconfiguration");
return await response.json();
}
catch (error) {
console.log(error);
}
}
async componentDidMount() {
...
var configuration = await GetConfiguration();
this.setState({ configuration: configuration });
...
}
render() {
...
<Router>
<Switch>
<Route exact path="/my-account">
<div className='dw-side-menu'></div>
...
</Route>
{this.state.configuration.shouldRoute && <Route exact path="/my-company"><div>my company</div></Route>}
{this.state.configuration.shouldRoute && <Route exact path="/user-management"><div>user management</div></Route>}
<Redirect to="/my-account" />
</Switch>
</Router>
...
}
The awkward thing is that, when I click on the Link somewhere else on the page, the routing to, e.g. /my-company, works - but not if I type the URL into the browser it only goes to the redirect, as if the routes are not there at all. Also if I hit refresh when on /my-company I get redirected back to the my-account. Without the conditions everything is working fine.
Without Redirect, entering the URLs and Refreshing the browser works as expected, just that I don't get redirected when a route is not recognized.
What am I doing wrong?
As suspected in my comment, this.state.configuration.shouldRoute is undefined on first render.
One workaround for this would be to actually display a loading state until you have loaded the configuration, like so:
// in render(), before your other return
if(Object.keys(this.state.configuration).length === 0){
// config has not been loaded yet
return (<MyCoolLoadingComponent />)
}
If displaying a loading indicator before the config is loaded is an option, go with it. If it is not, you have to rethink your architecture.
To make your code cleaner, with less bugs, put sections of codes into individual components. Then you could say:
class SideMenu extends component {
render(
<div className='dw-side-menu'></div>
...
)
}
class MyCompany extends component {
render(
<div>my company</div>
...
)
}
class UserManagement extends component {
render(
<div>user management</div>
...
)
}
render() {
...
<Router>
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/my-account" component={MyAccount} />
<Route path="/my-company" component={MyCompany} />
<Route path="/user-management" component={UserManagement} />
<Redirect to="/my-account" />
</Switch>
</Router>
...
}
Cleaner code with less issues. Hope this helps. You can as well use some of the powerful features in react-router-dom; location, history and match. Thanks.

Create Dynamic route and render component in reactjs

I am new in ReactJs.
I need a route like localhost:3000/directory/category/region/brandName and for the same route, I need to render a component
Sample of URL be like
localhost:3000/directory/photography/france/testA
localhost:3000/directory/Catering/germany/testB
for both above URLs, a component called name.js should render
You can make use of react-router and then configure your Routes by making use of Route params
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
const App () => {
return (
<Router>
<Switch>
<Route path="/directory/:profession/:country/:value" component={Name} />
<Route path="/" component={Dashboard}/>
</Switch>
)
}
Now post this you can access the params in name component and fetchData from api or have any other logic
const Name = () => {
const { profession, country, value} = useParams();
useEffect(() => {
// Any fetch logic based on params
}, [profession, country, value]);
return (
..
)
}
You can read more about react-router usage here and also refer the docs
As far as I understand from the question, you can handle this through using "Redirect" component. Let there be a "Navigation" component where the "Router" is defined as you did
Navigation.js
import Name from './name';
import From from './from';
<Router>
<Switch>
<Route path="/from">
<From />
</Route>
<Route path="/directory/:profession/:country/:value">
<Name />
</Route>
</Switch>
</Router>
and a "From" component where paths and redirections are defined. If "redirectionPath" is not null you can return "Redirect" component in render. Thus, you can redirect to and render the Name component.
From.js
import React, {Component} from 'react';
import {
Redirect
} from "react-router-dom";
class From extends Component {
state={
redirectionPath: "/directory/photography/france/testA" // or setState anywhere you need.
}
...
render(){
if(this.state.path){
return (<Redirect to={this.state.redirectionPath} />)
}
return (
<SomeComponent/>
);
}
}
This can be one of the solutions. Hope it works for you as well.

Protecting routes in React app with React Router

I've created a simple React app with Redux, React Router and Auth0 which handles user authentications.
I'm trying to create this basic behavior to control access:
All unauthenticated users will automatically be sent to /public
Authenticated users can access all the other parts of the app
Once a user is authenticated by Auth0, I want to process the access_token and send user to / which is the Home component
Everything is "almost" working the way it should. The problem I'm having is that render() function in App.jsx is executing BEFORE the lock.on('authenticated') listener even has a chance to process the tokens returned by Auth0. As a result, the tokens are never stored and the user always seems to be unauthenticated. If I send user to /login, everything works fine because I'm not checking to see if the user is authenticated before rendering the Login component.
I think the way I'm handling protected routes needs to change. Any suggestions as to how to handle protected routes?
I'm providing the code that you need here. If you want to see the whole app, go to https://github.com/imsam67/react-redux-react-router-auth0-lock
The following is the App.jsx:
class App extends Component {
render() {
const isAuthed = isAuthenticated();
return (
<div>
<Switch>
<Route exact path="/" render={ props => isAuthed ? <Home {...props} /> : <Redirect to="/public" /> } />
<Route exact path="/login">
<Login />
</Route>
<Route path="/public">
<Public />
</Route>
</Switch>
</div>
);
}
}
This is the AuthWrapper component where I handle Auth0:
class AuthWrapper extends Component {
constructor(props) {
super(props);
this.onAuthenticated = this.onAuthenticated.bind(this);
this.lock = new Auth0Lock('my_auth0_client_id', 'my_domain.auth0.com', {
auth: {
audience: 'https://my_backend_api_url',
redirectUrl: 'http://localhost:3000/',
responseType: 'token id_token',
sso: false
}
});
this.onAuthenticated();
}
onAuthenticated() {
debugger; // After successful login, I hit this debugger
this.lock.on('authenticated', (authResult) => {
debugger; // But I never hit this debugger
let expiresAt = JSON.stringify((authResult.expiresIn * 1000) + new Date().getTime());
sessionStorage.setItem('access_token', authResult.accessToken);
sessionStorage.setItem('id_token', authResult.idToken);
sessionStorage.setItem('expires_at', expiresAt);
});
}
render() {
return(
<AuthContext.Provider value={{ lock: this.lock }}>
{this.props.children}
</AuthContext.Provider>
);
}
}
And here's index.js in case you need to see it:
import App from './components/App';
import AuthWrapper from './components/auth/AuthWrapper';
// Store
import appStore from './store/app-store';
const store = appStore();
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<AuthWrapper>
<App />
</AuthWrapper>
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
Here are the changes I made to make this work.
I now initialize Auth0 Lock in index.js to make it global
I moved the onAuthenticated() listener to LoginCallback.jsx component which is where the user is sent after a successful login. I believe moving the onAuthenticated() from App.jsx to LoginCallback.jsx made the biggest impact because the listener in LoginCallback.jsx executes before App.jsx.
On successful authentication, I also use this.props.history.push('/'); to send user to Home component
For full code, please see the repo at https://github.com/imsam67/react-redux-react-router-auth0-lock
Remember nothing is protected in the client side at all. If your concerned of routing to a component without auth just make sure no data is exposed(assuming they can't get any data without a token) and redirect if they landed there even after your router checks for auth or shows an error.
Remember nothing is protected in the client side at all. If your concerned of routing to a component without auth just make sure no data is exposed and redirect if they landed there even after your router checks for auth. I think #Sam has it right. The routes may not respond as expected to an asynchronous call changing it or may have odd behavior. I've never attempted a dynamic route this way but always had conditional renders of content components. A better approach may be to send the call and in the then block redirect to a url which the router knows to handle. Just not sure the router handles this very well. Catch the component checking for auth on load and redirect back to log on if not authorized. Sorry I'm not helping much here but conditional routes almost seem like an anti pattern but I guess it could work if we knew how the router renders its data after changes or if it actually does at all(the routes them selves.) So if they were to bookmark the url and try to return back that would be a 404 right? Maybe like a 401 unauthorized showing and redirect or link to log in might be better?
Dynamic routing need to be defined outside of the <Switch> scope. Here is an exemple assuming your function isAuthenticated() is a state (Redux or wathever)
import React from "react";
import ReactDOM from "react-dom";
import { createBrowserHistory } from "history";
import { Router, Route, Switch, Redirect } from "react-router-dom";
// core components
import Admin from "layouts/Admin.js";
import SignIn from "layouts/SignIn";
const hist = createBrowserHistory();
const loggedRoutes = () => (
<Switch>
<Route path="/" component={SignIn} />
<Route path="/admin" component={Admin} />
<Redirect from="/admin" to="/admin/aboutUs/whatWeDo" />
</Switch>
);
const routes = () => (
<Switch>
<Route path="/" component={SignIn} />
<Route path="/login" component={Admin} />
<Redirect from="/" to="/login" />
</Switch>
);
ReactDOM.render(
<Router history={hist}>
{checkIfAuth? loggedRoutes():routes()}
</Router>,
document.getElementById("root")
);
In this exemple, If you are not login you are redirect to /login.

React Router v4 programmatic redirect: Warning when redirecting to the "same route"

I have a React app that has a basic structure like seen below. I am attempting to programmatically redirect the user to /profile route but I am seeing Warning: You tried to redirect to the same route you're currently on: "/profile".
Inside the Main component header I have a form that should redirect the user to /profile. The only way that I was able to get this working is to add && this.props.location.pathname !== '/profile' to the state.toProfile condition. This feels a bit dirty. It seems like there is a better way.
I am taking the approach recommended in this blog post https://tylermcginnis.com/react-router-programmatically-navigate/, it seems like this doesn't work if the route you redirect to contains the same component as the route that was redirected from.
class App extends Component {
render() {
return (
<BrowserRouter>
<Switch>
<Route path="/dashboard" component={Dashboard} />
<Route component={Main} />
</Switch>
</BrowserRouter>
);
}
}
class Main extends Component {
state = {
toProfile: false
}
render() {
if (this.state.toProfile === true) {
return <Redirect to='/profile' />
}
return (
<header>
...
</header>
);
}
}
you need to add a route for /profile, atm profile goes to Main that goes to /profile... again

React Router v4 - Redirect to home on page reload inside application

I need to redirect to home page when user refreshes other pages inside my application. I am using React router v4 and redux. Since the store is lost on reload, the page user reloaded is now empty and hence I want to take him back to a page that does not need any previous stored data. I don't want to retain state in localStorage.
I tried to handle this in event onload but it did not work:
window.onload = function() {
window.location.path = '/aaaa/' + getCurrentConfig();
};
You can try creating a new route component, say RefreshRoute and check for any state data you need. If the data is available then render the component else redirect to home route.
import React from "react";
import { connect } from "react-redux";
import { Route, Redirect } from "react-router-dom";
const RefreshRoute = ({ component: Component, isDataAvailable, ...rest }) => (
<Route
{...rest}
render={props =>
isDataAvailable ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/home"
}}
/>
)
}
/>
);
const mapStateToProps = state => ({
isDataAvailable: state.reducer.isDataAvailable
});
export default connect(mapStateToProps)(RefreshRoute);
Now use this RefreshRoute in your BrowserRouter as like normal Route.
<BrowserRouter>
<Switch>
<Route exact path="/home" component={Home} />
<RefreshRoute exact path="dashboard" component={Dashboard} />
<RefreshRoute exact path="/profile" component={ProfileComponent} />
</Switch>
</BrowserRouter>
It is so amazing that you don't want to keep state of user route map in browser but you use react-router!, the main solution for your case is do not use react-router.
If you don't use it, after each refresh the app come back to main view of app, If you wanna see route map in address bar without any reaction use JavaScript history pushState.
Hope it helps you.

Resources