In an application using react, redux and react-router, I'm using react-router-redux to issue navigation actions. I found that wrapping routes in a component with connect blocks navigation.
I made a sample with CodeSandbox that illustrates the issue: sample.
As is, the navigation doesn't work. However, if in ./components/Routes.jsx, this line:
export default connect(() => ({}), () => ({}))(Routes);
Is replaced by:
export default Routes;
It works.
Any idea how I could use connect in a component that wraps routes without breaking navigation?
See the troubleshooting section in react-redux docs.
If you change Routes.jsx export to:
export default connect(() => ({}), () => ({}), null, { pure: false })(Routes);
it will work.
This is because connect() implements shouldComponentUpdate by default,
assuming that your component will produce the same results given the
same props and state.
route changes, but props don't so the view doesn't update.
You could achieve same with withRouter hoc.
Not meant to be a duplicate.
I fixed it with withRouter like this
import { withRouter } from 'react-router-dom';
and
export default withRouter( connect(mapStateToProps)(App) );
See Redux, Router integration docs here
Have you ever encountered the warning message:
Warning: You cannot change <Router history>
Well use withRouter from react-router-dom
I have searched for this for so long because the Redux was recreating my App.jsx component which has <Route> </Route> as parents and this warning just freezes the routing in my app. I wanted to have React/Redux component, because I needed to pass authenticated props to the Route component, and redirect base on it, simple.
So import { withRouter } from 'react-router-dom'
and surround your component which is connected to redux with:
export default withRouter(connect(mapStateToProps)(App));
Something more:
Most of the times if you want to communicate with the router, takes some props, pass something else to it, get history, locations form it and you are using Redux in your app, surround this component with withRouter and you will have access to these properties as props.
Related
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 expecting props.onStateChange but props is empty object.
Props passed to the enclosed component of withAuthenticator HOC is empty.
import { withAuthenticator } from "#aws-amplify/ui-react";
export const App = withAuthenticator((props) => {
console.log('props',props) // {}
return (
<BrowserRouter>
......
</BrowserRouter>
);
});
The thing I want to accomplish is sign-out functionality. I tried Auth.signOut() .But this is just clearing the localStorage but redirecting to sign-in page is not happening.
I searched for such issue and found out that
When using the Auth.signOut from within the withAuthenticator it will
not sign out because it is only updating the session locally in
LocalStorage. You need to have a way to rerender the actual
withAuthenticator component.
https://github.com/aws-amplify/amplify-js/issues/1529
https://github.com/aws-amplify/amplify-js/issues/4643
Solution that provide includes using props.onStateChange but the props in my case is empty.
Where am I wrong?
After some R&D I found that
import { withAuthenticator } from "#aws-amplify/ui-react";
Here withAuthenticator HOC doesn't provide any props.
And this approach is used to use pre-built UI components
import { withAuthenticator } from "aws-amplify-react";
Here withAuthenticator HOC provides props
authState
authData
onStateChange.
And this approach is used to customize (create) our own UI.
I am new using react router v4. I have a link that runs a function in the onClick event and then redirects to a specific route.
This is the link:
<Link className={''}
to={'/test'}
onClick={this.testFunction}>To test</Link>
and the test function:
testFunction(event){
e.preventDefault();
// do some things...
this.props.history.push('/test');
}
This works but I need to write both times the "/test" route (in the Link component and in the function).
Is there a way of getting the "to" prop so I don't have to write it twice?
If you use "withRouter" in your component:
import {Link, withRouter} from 'react-router-dom';
...
export default withRouter(TestComponent);
you can access the route's path by using:
this.props.match.path
Use this in your code:
testFunction(event){
e.preventDefault();
// do some things...
this.props.history.push(this.props.match.path);
}
When you use "withRouter" in your component you can access the match, location and history props of the route.
withRouter official documentation
match official documentation
Hope it helps!
How can I access browserHistory on redux? I'm passing the browserHistory to a browserRouter from react-router. I want a reducer to listen for an action and the push another url. What's the best solution for this?
First, you'll need to have access to the Route props like history, location, and match in a component. You will have these props automatically if the rendered component was created by a Route component from react-router. Otherwise, you will need to use withRouter from react-router to decorate your component with the Route props.
Example using withRouter:
// MyComponent before
import { connect } from 'react-redux'
const MyComponent = (props) => (<div>{props.message}</div>)
export default connect()(MyComponent)
// MyComponent after
import { connect } from 'react-redux'
import { withRouter } from 'react-router-dom'
const MyComponent = (props) => (<div>{props.message}</div>)
export default withRouter(connect()(MyComponent))
In the after example, MyComponent you will have access to history under props.history (or this.props.history if in a class component).
After you have access to history in your component, you can redirect the app using props.history.push('/a/new/url'). It sounds like you want to use it in an action to change the state. If that's the case, I recommend passing in your reference to history into the action as an argument. Here is an example of an action which creates a new Post and then redirects to a list view of Posts in the same category. Note: I'm using redux-thunk in this example in order to be able to dispatch async actions.
In my PostForm component, I create a variable for history:
const rrHistory = this.props.history
Later, I pass this variable into the createPost action when the Save button is clicked:
createPost(model.title, model.body, model.author, model.category, rrHistory)
Then in the action code, after some async stuff finishes with the API server, we use the rrHistory variable reference to redirect to the categories list view:
rrHistory.push(`/${postCategory}/${newPost.id}`)
Note, that this is a working example, but it is far from perfect. You can also check out this SO Answer for other navigation ideas Programmatically navigate using react router V4
import { withRouter } from 'react-router-dom';
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Header));
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