React Router 4 and props.history.push - javascript

There's something driving me crazy in React, and I need your help. Basically, when the user clicks "My Profile", I want to redirect to the user's profile. To do that, I use the following snippet
viewProfile = () => {
console.log('My USER ID: ', this.props.userId);
this.props.history.push(`/profile/${this.props.userId}`);
}
This should work. However, although the URL is changing to the correct URL when 'My Profile' is clicked, the page isn't appearing. It just stays as the old page.
After googling for a while, I know it's something to do with redux ... However, I can't find a solution. (this.props.userId is coming from a Layout component, which is in turn getting it from redux store)
Here's my code:
// React
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
// Redux
import { connect } from 'react-redux';
import * as actions from '../../../store/actions/';
// Containers and Components
...// more imports
const styles = // styles
class NavBar extends Component {
handleAuthentication = () => {
if (this.props.isAuthenticated) {
this.props.history.push('/logout');
} else {
this.props.history.push('/login');
}
};
viewProfile = () => {
console.log('My USER ID: ', this.props.userId);
this.props.history.push(`/profile/${this.props.userId}`);
};
render() {
let buttons = (
<Auxillary>
{this.props.isAuthenticated ? (
<RaisedButton
label="MY PROFILE"
primary={true}
style={styles.button}
onClick={this.viewProfile} // THIS IS THE METHOD
/>
) : null}
<RaisedButton
backgroundColor={grey900}
label={this.props.isAuthenticated ? 'LOGOUT' : 'LOGIN'}
labelColor={grey50}
style={styles.button}
onClick={this.handleAuthentication}
/>
</Auxillary>
);
let itemSelectField = null;
if (this.props.location.pathname === '/items') {
itemSelectField = (
<ItemSelectField
onSelectTags={tags => this.props.filterItemsByTagName(tags)}
/>
);
}
let bar = null;
if (
this.props.location.pathname !== '/login' &&
this.props.location.pathname !== '/register'
) {
bar = (
<AppBar
style={styles.appBar}
title={itemSelectField}
iconElementLeft={
<img style={styles.logoHeight} src={Logo} alt="logo" />
}
iconElementRight={buttons}
/>
);
}
return <Auxillary>{bar}</Auxillary>;
}
}
const mapDispatchToProps = dispatch => {
return {
filterItemsByTagName: selectedTags =>
dispatch(actions.filterItemsByTagName(selectedTags))
};
};
export default withRouter(connect(null, mapDispatchToProps)(NavBar));
You can find the entire app here: https://github.com/Aseelaldallal/boomtown/tree/boomtown-backend-comm
I'm going crazy trying to fix this. help.

I think has to do with you using <BrowserRouter>. It creates its own history instance, and listens for changes on that. So a different instance will change the url but not update the <BrowserRouter>. Instead you can use ConnectedRouter and pass a history instance as
import createHistory from 'history/createBrowserHistory';
...
const history = createHistory();
...
<ConnectedRouter history={history}>
...
</ConnectedRouter>

You need to integrate the react-router-redux.
Its look like the updated state is not reflecting in the container as redux do shallow comparison to check whether the component need to be update or not.
import {Router} from 'react-router-dom';
import { ConnectedRouter, routerReducer, routerMiddleware } from 'react-router-redux';
import createHistory from 'history/createBrowserHistory';
const history = createHistory()
const middleware = routerMiddleware(history)
const rootReducer = combineReducers({
router: routerReducer,
//other reducers
});
const store = createStore(rootReducer,applyMiddleware(middleware));
<Provider store={store}>
<ConnectedRouter>
//routes
</ConnectedRouter>
</Provider>
Also,at reducers,add this snippet.
let updatedStore = JSON.parse(JSON.stringify(state));

Related

Getting error when I try to upgrade react-router v5 to V6

I m getting typescript error when I tried to upgraded React-router-dom v5 to v6, How can I fix this typescript error. below you can find the code Thanks in advance
`
export function withRouter(ui: React.ReactElement) {
const history = useNavigate();
const routerValues: any = {
history: undefined,
location: undefined
};
const result = (
<MemoryRouter>
{ui}
<Route
path="*"
element={({ history, location }) => {
routerValues.history = history;
routerValues.location = location;
return null;
}}
/>
</MemoryRouter>
enter image description here`
below you can find entire file code
`
import React from "react";
import { Reducer } from "#reduxjs/toolkit";
import { Provider } from "react-redux";
import { MemoryRouter, Route, useNavigate } from "react-router-dom";
import buildStore from "../redux/store";
export function withRedux(
ui: React.ReactElement,
reducer: {
[key: string]: Reducer;
},
initialState: any
) {
const store = buildStore(initialState, true);
const dispatchSpy = jest.spyOn(store, "dispatch");
return {
result: <Provider store={store}>{ui}</Provider>,
store,
dispatchSpy
};
}
export function withRouter(ui: React.ReactElement) {
const history = useNavigate();
const routerValues: any = {
history: undefined,
location: undefined
};
const result = (
<MemoryRouter>
{ui}
<Route
path="*"
element={({ history, location }) => {
routerValues.history = history;
routerValues.location = location;
return null;
}}
/>
</MemoryRouter>
);
return { result, routerValues };
}
`
I am passing history and location props which were work fine when I was using react router v5
here is the previous code :
`
const result = (
<MemoryRouter>
{ui}
<Route
path="*"
render={({ history, location }) => {
routerValues.history = history;
routerValues.location = location;
return null;
}}
/>
</MemoryRouter>
`
After update react router v6 I changed in my code because We know that v6 no longer support render keyword inside route So I Replace it
`
const result = (
<MemoryRouter>
{ui}
<Route
path="*"
element={({ history, location }) => {
routerValues.history = history;
routerValues.location = location;
return null;
}}
/>
</MemoryRouter>
);
`
But I don't have Idea in v6 How can I pass these props inside route
Try this:
export function withRouter(ui: React.ReactElement) {
const history = useNavigate();
const location = useLocation();
const routerValues: any = {
history: history,
location: location
};
const result = (
<MemoryRouter>
{ui}
</MemoryRouter>
);
return { result, routerValues };
}
Issues
The withRouter Higher Order Component/render function can't use the RRD hooks outside the router it is rendering.
react-router-dom#6 Route components don't take "route props" and the element prop takes a React.ReactNode, a.k.a. JSX. The "route props" should be passed as props to the component being rendered.
Solution
You'll need to create two components. One is a test render function that provides the MemoryRouter as a test wrapper, and the other is a correct withRouter HOC.
Example:
Create a custom render function that renders the component under test into a wrapper component that provides all the various contexts (routers, redux, etc)
import { render } from '#testing-library/react';
import { MemoryRouter } from 'react-router-dom';
const Wrappers = ({ children }) => (
<MemoryRouter>
{children}
</MemoryRouter>
);
const customRender = (ui: React.ReactElement, options: object) => {
return render(ui, { wrapper: Wrappers, ...options });
};
export { customRender as render };
See the RTL setup docs for more information on custom render functions.
Create separate withRouter HOC to only decorate older React Class components that can't use the RRD hooks directly. Here's an example Typescript implementation.
import { ComponentType } from 'react';
import {
Location,
NavigateFunction,
useLocation,
useParams
} from 'react-router-dom';
export interface WithRouterProps {
location: Location;
navigate: NavigateFunction;
params: ReturnType<typeof useParams>;
}
export const withRouter = <P extends object>(Component: ComponentType<P>) =>
(props: Omit<P, keyof WithRouterProps>) => {
const location = useLocation();
const params = useParams();
const navigate = useNavigate();
return (
<Component
{...props}
{...{ location, params, navigate }}
/>
);
};

How to get the react navigator state outside the root react navigator?

This is the thing:
I have a react-native application on mobile, and I'm trying to do some authority check action when the user has left my app and get back. I want to avoid doing an authority check on the login screen and considering the existing application component tree, I want to get the current route for me to use. Now, I'm using 5.x version react-navigation.
const App = () => {
useEffect(() => {
const handleAppStateChange = (): void => {
// Authority check action
};
AppState.addEventListener('change', handleAppStateChange);
}, []);
return (
<Provider store={store}>
<SafeAreaProvider>
<SafeAreaView>
<RootNavigator />
<OverlayActivityIndicator />
<ActionSheet />
</SafeAreaView>
</SafeAreaProvider>
</Provider>
);
}
Now I meet the problem:
I don't know how to do to get the current route outside the root navigator.
My last choice is integrating the current route into the redux store, and that will be making my application more complex.
Any other ideas?
You can refer this for getting navigation outside components
import { NavigationContainer } from '#react-navigation/native';
import { navigationRef } from './RootNavigation';
export default function App() {
return (
<NavigationContainer ref={navigationRef}>{/* ... */}</NavigationContainer>
);
}
//whereas
import * as React from 'react';
export const navigationRef = React.createRef();
export function navigate(name, params) {
navigationRef.current?.navigate(name, params);
}

Passing react-router-dom's Link into external library

I'm rendering components from my external (node_modules) pattern library. In my main App, I'm passing my Link instance from react-router-dom into my external libraries' component like so:
import { Link } from 'react-router-dom';
import { Heading } from 'my-external-library';
const articleWithLinkProps = {
url: `/article/${article.slug}`,
routerLink: Link,
};
<Heading withLinkProps={articleWithLinkProps} />
In my library, it's rendering the Link as so:
const RouterLink = withLinkProps.routerLink;
<RouterLink
to={withLinkProps.url}
>
{props.children}
</RouterLink>
The RouterLink seems to render correctly, and even navigates to the URL when clicked.
My issue is that the RouterLink seems to have detached from my App's react-router-dom instance. When I click Heading, it "hard" navigates, posting-back the page rather than routing there seamlessly as Link normally would.
I'm not sure what to try at this point to allow it to navigate seamlessly. Any help or advice would be appreciated, thank you in advance.
Edit: Showing how my Router is set up.
import React from 'react';
import { hydrate, unmountComponentAtNode } from 'react-dom';
import { AppContainer } from 'react-hot-loader';
import { Provider } from 'react-redux';
import { createBrowserHistory } from 'history';
import { ConnectedRouter } from 'react-router-redux';
import RedBox from 'redbox-react';
import { Route } from 'react-router-dom';
import { Frontload } from 'react-frontload';
import App from './containers/App';
import configureStore from './redux/store';
import withTracker from './withTracker';
// Get initial state from server-side rendering
const initialState = window.__INITIAL_STATE__;
const history = createBrowserHistory();
const store = configureStore(history, initialState);
const mountNode = document.getElementById('react-view');
const noServerRender = window.__noServerRender__;
if (process.env.NODE_ENV !== 'production') {
console.log(`[react-frontload] server rendering configured ${noServerRender ? 'off' : 'on'}`);
}
const renderApp = () =>
hydrate(
<AppContainer errorReporter={({ error }) => <RedBox error={error} />}>
<Provider store={store}>
<Frontload noServerRender={window.__noServerRender__}>
<ConnectedRouter onUpdate={() => window.scrollTo(0, 0)} history={history}>
<Route
component={withTracker(() => (
<App noServerRender={noServerRender} />
))}
/>
</ConnectedRouter>
</Frontload>
</Provider>
</AppContainer>,
mountNode,
);
// Enable hot reload by react-hot-loader
if (module.hot) {
const reRenderApp = () => {
try {
renderApp();
} catch (error) {
hydrate(<RedBox error={error} />, mountNode);
}
};
module.hot.accept('./containers/App', () => {
setImmediate(() => {
// Preventing the hot reloading error from react-router
unmountComponentAtNode(mountNode);
reRenderApp();
});
});
}
renderApp();
I've reconstructed your use case in codesandbox.io and the "transition" works fine. So maybe checking out my implementation might help you. However, I replaced the library import by a file import, so I don't know if that's the decisive factor of why it doesn't work without a whole page reload.
By the way, what do you mean exactly by "seamlessly"? Are there elements that stay on every page and should not be reloaded again when clicking on the link? This is like I implemented it in the sandbox where a static picture stays at the top on every page.
Check out the sandbox.
This is the example.js file
// This sandbox is realted to this post https://stackoverflow.com/q/59630138/965548
import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import { Heading } from "./my-external-library.js";
export default function App() {
return (
<div>
<img
alt="flower from shutterstock"
src="https://image.shutterstock.com/image-photo/pink-flowers-blossom-on-blue-600w-1439541782.jpg"
/>
<Router>
<Route exact={true} path="/" render={Welcome} />
<Route path="/article/coolArticle" component={CoolArticleComponent} />
</Router>
</div>
);
}
const Welcome = () => {
const articleWithLinkProps = {
url: `/article/coolArticle`,
routerLink: Link
};
return (
<div>
<h1>This is a super fancy homepage ;)</h1>
<Heading withLinkProps={articleWithLinkProps} />
</div>
);
};
const CoolArticleComponent = () => (
<div>
<p>This is a handcrafted article component.</p>
<Link to="/">Back</Link>
</div>
);
And this is the my-external-library.js file:
import React from "react";
export const Heading = ({ withLinkProps }) => {
const RouterLink = withLinkProps.routerLink;
return <RouterLink to={withLinkProps.url}>Superlink</RouterLink>;
};

React Router and Redux - Data on initial load

I have looked a lot of questions and answers on the site concerning an issue that I am experiencing.
I have the usual setup with React, React Router and Redux. My top level component is as follows.
// Imports
const reducers = {
main: baseReducer,
form: formReducer,
};
const reducer = combineReducers(reducers);
const store = createStore(
reducer,
applyMiddleware(thunk),
);
store.dispatch(actions.auth.setCurrentUser());
// store.dispatch(actions.api.fetchLineup());
ReactDOM.render(
<Provider store={store}>
<HashRouter>
<App />
</HashRouter>
</Provider>
,
document.getElementById('root')
);
Inside my App.jsx I have the following code:
import React from 'react';
import Main from './Main';
const App = () => (
<div>
<Main />
</div>
);
export default App;
My Main.jsx
import React from 'react';
import { Switch, Route } from 'react-router-dom';
import GroupingContainer from '../containers/Grouping';
import HomeContainer from '../containers/Home';
const Main = () => (
<main>
<Switch>
<Route exact path='/' component={HomeContainer} />
<Route path='/groupings/:id' component={GroupingContainer} />
</Switch>
</main>
);
export default Main;
And finally I have my Grouping.jsx and GroupingContainer.jsx
import React, { Component } from 'react';
function loadGrouping(props, groupingId) {
const grouping = props.main.groupings.find(
(g) => g.id === groupingId
);
if (!grouping) {
props.dispatch(api.fetchGrouping(groupingId));
}
}
class Grouping extends Component {
constructor(props) {
super(props);
loadGrouping(props, props.groupingId);
}
componentWillReceiveProps(nextProps) {
console.log('Next: ', nextProps);
if (nextProps.match && nextProps.match.params.id !== this.props.match.params.id) {
loadGrouping(this.props, nextProps.groupingId);
}
}
render() {
const grouping = this.props.main.groupings.find(
(lg) => lg.id === this.props.groupingId
);
return (
<div>
<h1>{grouping.name}</h1>
</div>
);
}
}
export default Grouping;
GroupingContainer.jsx
import { connect } from 'react-redux';
import Grouping from '../components/Grouping';
const mapStateToProps = (state, ownProps) => {
return {
groupingId: parseInt(ownProps.match.params.id, 10),
...state,
};
};
const mapDispatchToProps = (dispatch) => ({
dispatch,
});
const GroupingContainer = connect(
mapStateToProps,
mapDispatchToProps,
)(Grouping);
export default GroupingContainer;
After the request it fires another action that adds the returned grouping to the store and into an array of groups state.main.groups
I am having 2 problems. When I browse from the root path to one of the groupings, the following flow:
http://localhost:3000 -> http://localhost:3000/#/groupings/19
I receive the message: Uncaught TypeError: Cannot read property 'name' of undefined for a brief second until the API request finishes and populates the {grouping.name} and when I do a complete refresh of the page on a grouping URL http://localhost:3000/#/groupings/19 the application does not load at all and gives them same Uncaught TypeError: Cannot read property 'name' of undefined
I have been using React for around 2 months and have really started using API requests on Component loads. I can not really figure out where to place the API request properly to prevent the view rendering before it has finished and erroring out.
Any help would be appreciated!
Thanks!
Try to change render of Grouping Component like this.
render() {
const grouping = this.props.main.groupings.find(
(lg) => lg.id === this.props.groupingId
);
return (
<div>
<h1>{grouping ? grouping.name : ""}</h1>
</div>
);
}

How to use Redirect in version 5 of react-router-dom of Reactjs

I am using the last version react-router module, named react-router-dom, that has become the default when developing web applications with React. I want to know how to make a redirection after a POST request. I have been making this code, but after the request, nothing happens. I review on the web, but all the data is about previous versions of the react router, and no with the last update.
Code:
import React, { PropTypes } from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { Redirect } from 'react-router'
import SignUpForm from '../../register/components/SignUpForm';
import styles from './PagesStyles.css';
import axios from 'axios';
import Footer from '../../shared/components/Footer';
class SignUpPage extends React.Component {
constructor(props) {
super(props);
this.state = {
errors: {},
client: {
userclient: '',
clientname: '',
clientbusinessname: '',
password: '',
confirmPassword: ''
}
};
this.processForm = this.processForm.bind(this);
this.changeClient = this.changeClient.bind(this);
}
changeClient(event) {
const field = event.target.name;
const client = this.state.client;
client[field] = event.target.value;
this.setState({
client
});
}
async processForm(event) {
event.preventDefault();
const userclient = this.state.client.userclient;
const clientname = this.state.client.clientname;
const clientbusinessname = this.state.client.clientbusinessname;
const password = this.state.client.password;
const confirmPassword = this.state.client.confirmPassword;
const formData = { userclient, clientname, clientbusinessname, password, confirmPassword };
axios.post('/signup', formData, { headers: {'Accept': 'application/json'} })
.then((response) => {
this.setState({
errors: {}
});
<Redirect to="/"/> // Here, nothings happens
}).catch((error) => {
const errors = error.response.data.errors ? error.response.data.errors : {};
errors.summary = error.response.data.message;
this.setState({
errors
});
});
}
render() {
return (
<div className={styles.section}>
<div className={styles.container}>
<img src={require('./images/lisa_principal_bg.png')} className={styles.fullImageBackground} />
<SignUpForm
onSubmit={this.processForm}
onChange={this.changeClient}
errors={this.state.errors}
client={this.state.client}
/>
<Footer />
</div>
</div>
);
}
}
export default SignUpPage;
You have to use setState to set a property that will render the <Redirect> inside your render() method.
E.g.
class MyComponent extends React.Component {
state = {
redirect: false
}
handleSubmit () {
axios.post(/**/)
.then(() => this.setState({ redirect: true }));
}
render () {
const { redirect } = this.state;
if (redirect) {
return <Redirect to='/somewhere'/>;
}
return <RenderYourForm/>;
}
You can also see an example in the official documentation: https://reacttraining.com/react-router/web/example/auth-workflow
That said, I would suggest you to put the API call inside a service or something. Then you could just use the history object to route programatically. This is how the integration with redux works.
But I guess you have your reasons to do it this way.
Here a small example as response to the title as all mentioned examples are complicated in my opinion as well as the official one.
You should know how to transpile es2015 as well as make your server able to handle the redirect. Here is a snippet for express. More info related to this can be found here.
Make sure to put this below all other routes.
const app = express();
app.use(express.static('distApp'));
/**
* Enable routing with React.
*/
app.get('*', (req, res) => {
res.sendFile(path.resolve('distApp', 'index.html'));
});
This is the .jsx file. Notice how the longest path comes first and get's more general. For the most general routes use the exact attribute.
// Relative imports
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter, Route, Switch, Redirect } from 'react-router-dom';
// Absolute imports
import YourReactComp from './YourReactComp.jsx';
const root = document.getElementById('root');
const MainPage= () => (
<div>Main Page</div>
);
const EditPage= () => (
<div>Edit Page</div>
);
const NoMatch = () => (
<p>No Match</p>
);
const RoutedApp = () => (
<BrowserRouter >
<Switch>
<Route path="/items/:id" component={EditPage} />
<Route exact path="/items" component={MainPage} />
<Route path="/yourReactComp" component={YourReactComp} />
<Route exact path="/" render={() => (<Redirect to="/items" />)} />
<Route path="*" component={NoMatch} />
</Switch>
</BrowserRouter>
);
ReactDOM.render(<RoutedApp />, root);
React Router v5 now allows you to simply redirect using history.push() thanks to the useHistory() hook:
import { useHistory } from "react-router-dom"
function HomeButton() {
let history = useHistory()
function handleClick() {
history.push("/home")
}
return (
<button type="button" onClick={handleClick}>
Go home
</button>
)
}
Simply call it inside any function you like.
this.props.history.push('/main');
Try something like this.
import React, { PropTypes } from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { Redirect } from 'react-router'
import SignUpForm from '../../register/components/SignUpForm';
import styles from './PagesStyles.css';
import axios from 'axios';
import Footer from '../../shared/components/Footer';
class SignUpPage extends React.Component {
constructor(props) {
super(props);
this.state = {
errors: {},
callbackResponse: null,
client: {
userclient: '',
clientname: '',
clientbusinessname: '',
password: '',
confirmPassword: ''
}
};
this.processForm = this.processForm.bind(this);
this.changeClient = this.changeClient.bind(this);
}
changeClient(event) {
const field = event.target.name;
const client = this.state.client;
client[field] = event.target.value;
this.setState({
client
});
}
processForm(event) {
event.preventDefault();
const userclient = this.state.client.userclient;
const clientname = this.state.client.clientname;
const clientbusinessname = this.state.client.clientbusinessname;
const password = this.state.client.password;
const confirmPassword = this.state.client.confirmPassword;
const formData = { userclient, clientname, clientbusinessname, password, confirmPassword };
axios.post('/signup', formData, { headers: {'Accept': 'application/json'} })
.then((response) => {
this.setState({
callbackResponse: {response.data},
});
}).catch((error) => {
const errors = error.response.data.errors ? error.response.data.errors : {};
errors.summary = error.response.data.message;
this.setState({
errors
});
});
}
const renderMe = ()=>{
return(
this.state.callbackResponse
? <SignUpForm
onSubmit={this.processForm}
onChange={this.changeClient}
errors={this.state.errors}
client={this.state.client}
/>
: <Redirect to="/"/>
)}
render() {
return (
<div className={styles.section}>
<div className={styles.container}>
<img src={require('./images/lisa_principal_bg.png')} className={styles.fullImageBackground} />
{renderMe()}
<Footer />
</div>
</div>
);
}
}
export default SignUpPage;
Update for react-router-dom v6, there is a useNavigate hook for condtional redirection and Link component
import { useEffect } from 'react';
import { useNavigate, Link } from 'react-router-dom';
export default function Example(): JSX.Element {
const navigate = useNavigate();
useEffect(() => {
...
if(true) { // conditional redirection
navigate('/not-found', { replace: true });
}
}, []);
return (
<>
...
<Link to="/home"> Home </Link> // relative link navigation to /home
...
</>
);
}
useNavigate
Relative Link Component
Alternatively, you can use withRouter. You can get access to the history object's properties and the closest <Route>'s match via the withRouter higher-order component. withRouter will pass updated match, location, and history props to the wrapped component whenever it renders.
import React from "react"
import PropTypes from "prop-types"
import { withRouter } from "react-router"
// A simple component that shows the pathname of the current location
class ShowTheLocation extends React.Component {
static propTypes = {
match: PropTypes.object.isRequired,
location: PropTypes.object.isRequired,
history: PropTypes.object.isRequired
}
render() {
const { match, location, history } = this.props
return <div>You are now at {location.pathname}</div>
}
}
// Create a new component that is "connected" (to borrow redux
// terminology) to the router.
const ShowTheLocationWithRouter = withRouter(ShowTheLocation)
Or just:
import { withRouter } from 'react-router-dom'
const Button = withRouter(({ history }) => (
<button
type='button'
onClick={() => { history.push('/new-location') }}
>
Click Me!
</button>
))
The problem I run into is I have an existing IIS machine. I then deploy a static React app to it. When you use router, the URL that displays is actually virtual, not real. If you hit F5 it goes to IIS, not index.js, and your return will be 404 file not found. How I resolved it was simple. I have a public folder in my react app. In that public folder I created the same folder name as the virtual routing. In this folder, I have an index.html with the following code:
<script>
{
sessionStorage.setItem("redirect", "/ansible/");
location.href = "/";
}
</script>
Now what this does is for this session, I'm adding the "routing" path I want it to go. Then inside my App.js I do this (Note ... is other code but too much to put here for a demo):
import React, { Component } from "react";
import { Route, Link } from "react-router-dom";
import { BrowserRouter as Router } from "react-router-dom";
import { Redirect } from 'react-router';
import Ansible from "./Development/Ansible";
import Code from "./Development/Code";
import Wood from "./WoodWorking";
import "./App.css";
class App extends Component {
render() {
const redirect = sessionStorage.getItem("redirect");
if(redirect) {
sessionStorage.removeItem("redirect");
}
return (
<Router>
{redirect ?<Redirect to={redirect}/> : ""}
<div className="App">
...
<Link to="/">
<li>Home</li>
</Link>
<Link to="/dev">
<li>Development</li>
</Link>
<Link to="/wood">
<li>Wood Working</li>
</Link>
...
<Route
path="/"
exact
render={(props) => (
<Home {...props} />
)}
/>
<Route
path="/dev"
render={(props) => (
<Code {...props} />
)}
/>
<Route
path="/wood"
render={(props) => (
<Wood {...props} />
)}
/>
<Route
path="/ansible/"
exact
render={(props) => (
<Ansible {...props} checked={this.state.checked} />
)}
/>
...
</Router>
);
}
}
export default App;
Actual usage: chizl.com
EDIT: changed from localStorage to sessionStorage. sessionStorage goes away when you close the tab or browser and cannot be read by other tabs in your browser.
NOTE: Answering just the title of the question
Previous Version
<Redirect from="/old-url" to="/new-url" />
Latest version
<Route path="/old-url" element={<Navigate to="/new-url" />} />
In v6 of react-router you can accomplish this using <Navigate/> tag as there is no <Redirect/> Component.
In my case. I was required to maintain the connection to the server between /Home route and /chat route; setting window.location to something would re-render that destroys client-server connection I did this.
<div className="home-container">
{redirect && <Navigate to="/chat"/>}
<div className="home__title">
....
<div className="home__group-list" onClick={handleJoin}>
</div>
const [redirect, doRedirect] = useState(false)
handleJoin changes the state of redirect to true.
you can write a hoc for this purpose and write a method call redirect, here is the code:
import React, {useState} from 'react';
import {Redirect} from "react-router-dom";
const RedirectHoc = (WrappedComponent) => () => {
const [routName, setRoutName] = useState("");
const redirect = (to) => {
setRoutName(to);
};
if (routName) {
return <Redirect to={"/" + routName}/>
}
return (
<>
<WrappedComponent redirect={redirect}/>
</>
);
};
export default RedirectHoc;
"react": "^16.3.2",
"react-dom": "^16.3.2",
"react-router-dom": "^4.2.2"
For navigate to another page (About page in my case), I installed prop-types. Then I import it in the corresponding component.And I used this.context.router.history.push('/about').And it gets navigated.
My code is,
import React, { Component } from 'react';
import '../assets/mystyle.css';
import { Redirect } from 'react-router';
import PropTypes from 'prop-types';
export default class Header extends Component {
viewAbout() {
this.context.router.history.push('/about')
}
render() {
return (
<header className="App-header">
<div className="myapp_menu">
<input type="button" value="Home" />
<input type="button" value="Services" />
<input type="button" value="Contact" />
<input type="button" value="About" onClick={() => { this.viewAbout() }} />
</div>
</header>
)
}
}
Header.contextTypes = {
router: PropTypes.object
};
Alternatively, you can use React conditional rendering.
import { Redirect } from "react-router";
import React, { Component } from 'react';
class UserSignup extends Component {
constructor(props) {
super(props);
this.state = {
redirect: false
}
}
render() {
<React.Fragment>
{ this.state.redirect && <Redirect to="/signin" /> } // you will be redirected to signin route
}
</React.Fragment>
}
Hi if you are using react-router v-6.0.0-beta or V6 in This version Redirect Changes to Navigate like this
import { Navigate } from 'react-router-dom'; // like this CORRECT in v6
import { Redirect } from 'react-router-dom'; // like this CORRECT in v5
import { Redirect } from 'react-router-dom'; // like this WRONG in v6
// This will give you error in V6 of react-router and react-router dom
please make sure use both same version in package.json
{
"react-router": "^6.0.0-beta.0", //Like this
"react-router-dom": "^6.0.0-beta.0", // like this
}
this above things only works well in react Router Version 6
The simplest solution to navigate to another component is( Example
navigates to mails component by click on icon):
<MailIcon
onClick={ () => { this.props.history.push('/mails') } }
/>
To navigate to another component you can use this.props.history.push('/main');
import React, { Component, Fragment } from 'react'
class Example extends Component {
redirect() {
this.props.history.push('/main')
}
render() {
return (
<Fragment>
{this.redirect()}
</Fragment>
);
}
}
export default Example
I found that place to put the redirect complent of react-router is in the method render, but if you want to redirect after some validation, by example, the best way to redirect is using the old reliable, window.location.href, i.e.:
evalSuccessResponse(data){
if(data.code===200){
window.location.href = urlOneSignHome;
}else{
//TODO Something
}
}
When you are programming React Native never will need to go outside of the app, and the mechanism to open another app is completely different.

Categories