Reputation:
I am using react router 4 not react router 3 so I am using react-router-dom. I am trying to get this.props.history.push to work, but all it is doing is keeping the view the same, but the URL changes.
For example I click the button and it calls a function which just calls this.props.history.push('/posts'). The url changes from localhost:3000/signin to localhost:3000/posts. But I am still on my signin view.
If I hit refresh however it will redirect to the posts page. I have looked at other questions and those people could not get it to go there no matter what, even if they refreshed or went into the url and hit enter.
I will post the code below to see what I am doing wrong. Also I am using react router v4 not 3. I cannot use browserHistory.push and other things that v3 used.
Code:
component:
import React, { Component } from 'react';
import {Field, reduxForm} from 'redux-form';
import {connect} from 'react-redux';
import * as actions from '../../actions';
import {withRouter} from 'react-router';
class Signin extends Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
}
renderField(field) {
const { meta: {touched, error} } = field;
const className = `form-group ${touched && error ? 'has-danger' : ''}`;
return (
<div className={className}>
<label><strong>{field.label}:</strong></label>
<input
className="form-control"
type={field.type}
{...field.input}
/>
<div className="text-help">
{ touched ? error : ''}
</div>
</div>
)
}
onSubmit({email, password}) {
// this.props.signinUser({email, password}, () => {
this.props.history.push('/posts');
// });
}
renderAlert() {
if(this.props.errorMessage) {
return (
<div className="alert alert-danger">
<strong>Oops</strong> {this.props.errorMessage}
</div>
)
}
}
render() {
const { handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit(this.onSubmit)}>
<Field
label="Email"
name="email"
type="email"
component={this.renderField}
/>
<Field
label="Password"
name="password"
type="password"
component={this.renderField}
/>
{this.renderAlert()}
<button type="submit" className="btn btn-success">Sign In</button>
</form>
);
}
}
function validate(values) {
const errors = {};
//Validate the inputs from values
if(!values.email) {
errors.email = "Enter an email!";
}
if(!values.password) {
errors.password = "Enter a password!";
}
//If errors is empty, the form is fine to submit
//If errors has any properties, redux form assumes form is invalid
return errors;
}
function mapStateToProps(state) {
return {
errorMessage: state.auth.error
};
}
export default reduxForm({
validate,
form: 'signin'
})(connect(mapStateToProps, actions)(withRouter(Signin)));
action:
export function signinUser({email, password}, cb) {
return function(dispatch) {
axios.post(`${ROOT_URL}/signin`, {email, password})
.then((response) => {
dispatch({type: AUTH_USER});
localStorage.setItem('token', response.data.token);
})
.then(() => cb())
.catch((error) => {
dispatch(authError(error.response.data.error));
})
}
}
Upvotes: 3
Views: 2044
Reputation:
I found out the problem was not within the code I posted but how I was doing my routes. Make sure when you are routing you are not nesting routes as components as your routes. For example I had:
const Routes = (
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
)
Here I had imported App as a component (or it was exported as a component), I changed it from a component to a normal function. To make this more clear I will put old code and new code below:
Old:
class App extends React.Component {
render() {
return (
<div>
<Switch>
<Route exact path='/' component={Home} />
<Route path="/signin" component={RequireUnAuth(Signin)} />
<Route path="/signout" component={Signout} />
<Route path="/signup" component={Signup} />
<Route path="/posts" component={Posts} />
</Switch>
</div>
)
}
}
New:
const App = () => (
<div>
<Switch>
<Route exact path='/' component={Home} />
<Route path="/signin" component={RequireUnAuth(Signin)} />
<Route path="/signout" component={Signout} />
<Route path="/signup" component={Signup} />
<Route path="/posts" component={Posts} />
</Switch>
</div>
)
This is the App that was being exported and once I switched to a simple function instead of a component based class everything worked. I didn't have to do hard refreshes, I can click back and it goes back, redirecting with this.props.history.push works, etc...
Upvotes: 3
Reputation: 3583
Make sure you have the latest version of react-router-redux installed (https://github.com/reactjs/react-router-redux), with React Router 4 support. In package.json:
"react-router-redux": "next"
Next, follow the instructions on adding history middleware to your redux store. Link: https://github.com/reactjs/react-router-redux#what-if-i-want-to-issue-navigation-events-via-redux-actions
Once you have the correct package, and middleware installed, you need to import the reducer into your index reducer e.g.:
import {combineReducers} from 'redux';
import {routerReducer} from 'react-router-redux';
const IndexReducer = combineReducers({
router: routerReducer,
//...other reducers go here
});
export default IndexReducer;
Then, import the push method wherever you would like to dispatch it (e.g. actions.js):
import {push} from 'react-router-redux';
And then use it like so:
dispatch(push('/your/route/here'));
Upvotes: 0