I've updated react-router to the latest version - 2.4.1
I use hashHistory in my app
import { Router, useRouterHistory } from 'react-router'
import { createHashHistory } from 'history'
// useRouterHistory creates a composable higher-order function
const appHistory = useRouterHistory(createHashHistory)({ queryKey: false })
<Router history={appHistory}/>
And now I'm getting an error
Warning: Using { queryKey: false } no longer works.
Instead, just don't use location state if you don't want a key in your URL query string
How can I fix this?
I don't want additional query key to appear in url. I expect the same behaviour as from angular router or backbone router.
This warning does not actually originate from React Router but the history module it uses. In fact, the latest version (3.0.0, May 30 2016) doesn’t work at all with React Router:
Uncaught Invariant Violation: You have provided a history object created with history v3.x. This version of React Router is not compatible with v3 history objects. Please use history v2.x instead.
This warning is present as of v2.5.1 (Jun 24 2016).
So the easiest solution is to just use the same version React Router uses, courtesy of its package.json:
"history": "^2.0.1"
After that, the queryKey option works as expected.
You can use const history = useRouterHistory(createHashHistory)(); but then the refresh or back button will result in white page. It works very similar to browserHistory which requires mod_rewrite or middleware to function properly.
Maybe someone knows why they changed it like this because for me it doesn't make sense.
This worked for me
const appHistory = useRouterHistory(createHashHistory)();
Related
Context
I'm working with a hybrid next.js and react-router app. Parts of the app are handled by react-router (hash-based), and parts of it by next.js router. There are common components which use hooks related to the current routing state (e.g. useLocation), which crash if the react-router provider wrapper is missing.
Problem
I would like to write a hook that returns either useLocation (from react-router-dom) or useRouter (from next.js), depending on whether it detect the react-router provider in the current context.
Then I would use this hook in common components, so that they work regardless of which context they're used in.
There is a similar solution for detecting whether to use useEffect or useLayoutEffect for SSR, called useIsomorphicLayoutEffect. I'm thinking that a similar approach could work in my case. However, feel free to suggest different solutions.
The error I'm getting is TypeError: useContext(...) is undefined. The react-router wrapper provides a context which is used by the useLocation hook. Therefore I believe a generic solution for detecting the context provider would be valid here.
Example
const fooCommonComponent = () => {
// ❌ this only works when react-router-dom provider exists in the current context
const { pathname } = useLocation();
// ❌ this only works for next.js router
const { pathname } = useRouter();
// ✅ what i want
const { pathname } = useCustomLocation();
};
const useCustomLocation = () => {
// how to implement this?
};
I'm not very familiar with the Next.js side of things, but react-router-dom#6 has an useInRouterContext hook to return true/false if the component is rendered within a RRD routing context.
useInRouterContext
The useInRouterContext hooks returns true if the component is
being rendered in the context of a <Router>, false otherwise. This
can be useful for some 3rd-party extensions that need to know if they
are being rendered in the context of a React Router app.
Here's an example implementation that works for at least the RRD side of things.
import { useInRouterContext, useLocation } from "react-router-dom";
import { useRouter } from "next/router";
const useCustomLocation = () => {
const isInRRDContext = useInRouterContext();
return (isInRRDContext ? useLocation : useRouter)() ?? {};
};
Is it possible to create a global history file to manage the createBrowserHistory() on react-router-dom v5?
I know the V5 has the useHistory() as a way to get the history. But is it possible to retrieve the history from anywhere, like for cases where I am not using a function component?
On V4 I could create a file history.js:
import { createBrowserHistory } from 'history';
export default createBrowserHistory();
It works on V4
https://codesandbox.io/s/react-router-v4-nfwr0
It doesn't work on V5 - It updates the URL but redirects to not-found
https://codesandbox.io/s/react-router-v5-not-working-jlrep
As the doc says you should use the v4 of history to work on react-router v5.
https://github.com/ReactTraining/history
Documentation for version 4 can be found on the v4 branch. Version 4 is used in React Router versions 4 and 5.
I solved this by doing this.See what i done
Create a file like before
This is the code
import {createBrowserHistory} from 'history';
import store from 'store';
export default createBrowserHistory({
basename: store ? store.getState().productionBaseUrl : ''
});
The reason why i import Redux is the project does not pulish on nginx root folder.So i should add a basename.If you do not neet it,you can remove it.
Then you can use it in your own coponnet. How to use? Let me show your the code.
// if my history in src/router/
import history from 'router/history';
history.push(`${your router address}`)
Attention
The history's push method can pass an object like the origin props.But it's refresh when in the child router always. So pass a string when use it.
Code Sandbox link:
and trying to follow this article
On successful login(/auth/login), the user should be routed to the dashboard(/admin/summary). If the login is successful, I am also storing an access token.
I have a PrivateRoute component for this. The problem is that on successful login, the URL is getting updated but the component is not getting rendered.
PS: about the dashboard, this is a single page application so, the dashboard has topbar, sidebar, and the right content and altogether these things are coupled inside <AdminLayout/>. So, in my AppRouter, I have to render the <AdminLayout/> and just any one component.
All the react and redux code is included in the code sandbox.
Since in your code you create your own history object (it happens in you history.js file, when you call createBrowserHistory()) but doesn't pass it to your Router, nothing happens.
There are 2 possible solutions:
1. Don't create a history object yourself, but use useHistory hook inside your component
Working Demo
With this approach, you should remove history.push from login.actions.js (which imports history) and use history.push in Login.js (which uses useHistory hook):
// login.actions.js
...
loginService.login(userid, password, rememberPassword).then(
(userid) => {
dispatch(success(userid, password, rememberPassword));
// history.push(from); <-- commented out!
},
(error) => { ... }
);
};
...
// Login.js
function handleSubmit(e) {
...
const { from } = {
from: { pathname: "/admin/summary" }
};
history.push(from) // <-- added!
dispatch(loginActions.login(inputs, from));
...
}
useHistory exposes the history object of BrowserRouter (I think this is implied in this official blog post).
2. Create a history object yourself, but pass it to a Router component
Working Demo
This approach would require you to make several changes:
Creating the history object on your own means you become responsible to provide it to a router component, but it can't be a BrowserRouter, but the base Router component (see these Github answers: 1, 2).
Once you import Router (instead of BrowserRouter), you need to get rid of any useLocation and useHistory imports, otherwise you'll get errors.
I also had to unify the history object export and imports, so that it is exported as the default export (i.e., export default history), and it is imported as the default import (i.e., import history from "./history"; instead of import { history } from "./history")
(P.S: this approach can be seen implemented elsewhere on SO, for example here or here (the latter explicitly installs history, but it's not needed in your case).
I am trying to understand the difference between BrowserRouter and Router of the react-router-dom (v5) package and what difference it makes for my example below.
The documentation says:
BrowserRouter
A that uses the HTML5 history API (pushState,
replaceState and the popstate event) to keep your UI in sync with the
URL.
Source: https://reacttraining.com/react-router/web/api/BrowserRouter
Router
The common low-level interface for all router components. Typically
apps will use one of the high-level routers instead: BrowserRouter, HashRouter, MemoryRouter, NativeRouter, StaticRouter
Source: https://reacttraining.com/react-router/web/api/Router
From what I understand is that I should be using BrowserRouter for my HTML5 browser apps and I have been doing this so far.
history.push(...) example:
I am trying to perform a history.push('/myNewRoute') within a thunk:
import history as './history';
...
export function someAsyncAction(input) {
return dispatch => {
fetch(`${API_URL}/someUrl`, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({ input }),
}).then(() => {
history.push('/myNewRoute');
}).catch((err) => {
dispatch(setError(err));
})
};
};
history is defined as this module:
import { createBrowserHistory } from 'history';
export default createBrowserHistory();
and the history is also passed to my router:
import { BrowserRouter as Router } from 'react-router-dom';
import history as './history';
...
const App = () => (
<Router history={history}>
...
</Router>
);
Problem: history.push() will update the URL in the browser bar but not render the component behind the route.
If I import Router instead of BrowserRouter, it works:
// Does not work:
import { BrowserRouter as Router } from 'react-router-dom';
// Does work:
import { Router } from 'react-router-dom';
BrowserRouter ignores the history prop as it handles the history automatically for you. If you need access to the history outside of a react component, then using Router should be fine.
You can access history via the useHistory hook let history = useHistory(); to perform history.push() for BrowserRouter.
Looking at the HTML5 History API documentation, it seems that the history API preserves state for the user automatically. Say you are at page 1 initially and page 1 has a page outlook A. You performed some actions that changes the page 1 outlook to B. If you now moves to page 2, when you click the back button on the browser, you will be direct back to page 1. History API preseves your state so it knows to render outlook B to you, so that is the advantage of using BrowserRouter. Though I am not 100% sure, I suppose Browser doesn't come with this functionality and in which case it will render outlook A when you get directed back to page 1. This is not true. I am not sure about the difference.
I have the same issue.
BrowserRouter and useHistory() hook have been used for my component. And createBrowserHistory() has been used for redux-saga. But, the page has not moved by redux-saga such as your case.
Adding to that, my source has been developed using BrowserRouter, I don't want to replace it to Router component.
As my poor investigating, I found that both history objects are different. (I compared them with if and ==.) I guess it is the reason.
To solve it, I save the reference of the history object got by useHistory() to some global utility code, and use it in redux-saga code. Then, it works well.
I don't think this is the best way, but I couldn't find the best and official way yet.
Wondering how to go back to a previous route in a react web app using hashRouter instead of browserRouter in react-router v4?
I've found this question which doesn't seem to work even though it says no browser mixin needed (plus I think its talking about an older react-router version) however, every other solution I've seen depends on browserHistory.
Is it possible with hashHistory?
this.props.history.goBack()
Taken from the comments on this question
It is a function call.
Well in my case i did like that :
import withRouter from "react-router-dom/es/withRouter";
import React from "react";
class Component extends React.Component {
goBack() {
this.props.history.go(-1);
}
...
}
const WrappedComponent = withRouter(Component)
export default WrappedComponent;
withRouter give us access to history in props of component, but i'm not sure is this way is correct