I am trying to implement react router version 4. Please find the bare minimum code which I have now as below:
import React from 'react';
import { BrowserRouter, Route, Link } from 'react-router-dom';
import { Menu, MenuItem } from '#progress/kendo-layout-react-wrapper';
import { Switch } from 'react-router-dom';
export default () => (
<BrowserRouter>
<div>
<div className="col-xs-12 col-sm-6 col-md-12">
<header className="App-header">
<h1 className="App-title">TestUsers</h1>
</header>
<Menu>
<MenuItem>
<Link to="/users">Users</Link>
</MenuItem>
<MenuItem>
Shelves
</MenuItem>
<MenuItem>
Products
</MenuItem>
</Menu>
</div>
<Switch>
<Route exact path="/users" component={Users} />
<Route exact path="/users/add" component={Users} />
<Route exact path="/users/:id" component={Users} />
</Switch>
</div>
</BrowserRouter>
)
I have been able to add a user successfully. I want to redirect to user's list page from the action which adds a user. Please find the action code below:
export function addUser(objData) {
return function (dispatch) {
axios.post(
'http://localhost:4000/api/v1/users',
{
'name': objData.name,
'email': objData.email
}
)
.then((response) => {
dispatch({ 'type': ADD_USER, 'payload': true });
// TODO: programmatically redirect using react-router v4
})
.catch((error) => {
console.log(error);
});
}
}
I have been struggling to implement the same. Could anyone please point me in the right direction.
Thanks
You can Use Callback
export function addUser(objData, onSuccess, onFail) {
return function (dispatch) {
return axios.post(
'http://localhost:4000/api/v1/users',
{
'name': objData.name,
'email': objData.email
}
)
.then((response) => {
dispatch({ 'type': ADD_USER, 'payload': true });
onSuccess()
})
.catch((error) => {
//you can handle error on your component
onFail(error);
});
}
}
Then Call addUser like this on the component.
addUser(
objData,
() => this.props.history.push('/your-user-list-component'),
() => {// handle the error here.}
)
you could return Promise from your action:
export function addUser(objData) {
return function (dispatch) {
return axios.post(
'http://localhost:4000/api/v1/users',
{
'name': objData.name,
'email': objData.email
}
)
.then((response) => {
dispatch({ 'type': ADD_USER, 'payload': true });
// TODO: programmatically redirect using react-router v4
})
.catch((error) => {
console.log(error);
});
}
}
then in a component:
componentDidMount(){
this.props.addUser(objData).then(() => {
this.props.history.push('/somwhere')
})
}
use this.props.history.push('/some/path') after dispatch action
I would also advise you to check if response.status is equal to 200
You can use the history module.
Create history.js file
//history.js
import createHistory from 'history/createHashHistory'
export default createHistory()
Update your router with the new history. You need to use Router instead of BrowserRouter.
import React from 'react';
import { Router, Route, Link } from 'react-router-dom';
import { Menu, MenuItem } from '#progress/kendo-layout-react-wrapper';
import { Switch } from 'react-router-dom';
import history from './history';
export default () => (
<Router history={history}>
...
</Router>
);
And now you can navigate programmatically.
import history from './history'
export function addUser(objData) {
return function (dispatch) {
axios.post(
'http://localhost:4000/api/v1/users',
{
'name': objData.name,
'email': objData.email
}
)
.then((response) => {
dispatch({ 'type': ADD_USER, 'payload': true });
// TODO: programmatically redirect using react-router v4
history.push(path)
})
.catch((error) => {
console.log(error);
});
}
}
In react-router 4, there was introduced <Redirect /> component, which is returned in the render() method to achieve what you need. The redirect component is also imported from react-router-dom so: import { BrowserRouter, Route, Link, Redirect } from 'react-router-dom';
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/>;
}
Related
I am new to reactJS. I am trying to navigate to the specific room/roomcode page which I know exists. I have gone to the room/roomcode page to manually verify this. What I was wondering is how to use this hook in this class, or if I can not use it what would be the way around this error?
import React, { Component } from "react";
import { TextField, Button, Grid, Typography } from "#material-ui/core";
import { Link } from "react-router-dom";
export default class RoomJoinPage extends Component {
constructor(props) {
super(props);
this.state = {
roomCode: "",
error: "",
};
this.handleTextFieldChange = this.handleTextFieldChange.bind(this);
this.roomButtonPressed = this.roomButtonPressed.bind(this);
}
handleTextFieldChange(e) {
this.setState({
roomCode: e.target.value,
});
}
roomButtonPressed() {
const requestOptions = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
code: this.state.roomCode,
}),
};
fetch("/api/join-room", requestOptions)
.then((response) => {
if (response.ok) {
this.props.navigate("/room/" + this.state.roomCode);
} else {
this.setState({ error: "Room not found." });
}
})
.catch((error) => {
console.log(error);
});
}
}
This is my Homepage.js which is directly called by App.js:
import React, { Component } from "react";
import RoomJoinPage from "./RoomJoinPage";
import CreateRoomPage from "./CreateRoomPage";
import Room from "./Room";
import {
BrowserRouter as Router,
Routes,
Route,
Link,
Redirect,
} from "react-router-dom";
export default class HomePage extends Component {
constructor(props) {
super(props);
}
render() {
return (
<Router>
<Routes>
<Route exact path="/" element={<p>This is the home page</p>} />
<Route path="/join" element={<RoomJoinPage />} />
<Route path="/create" element={<CreateRoomPage />} />
<Route path="/room/:roomCode" element={<Room />} />
</Routes>
</Router>
);
}
}
Nothing in the HomePage component is using the useNavigate hook or calling any navigate function.
In react-router-dom#6 there are no longer any "route props" like there were in RRDv5, location, navigate, etc. are now all accessed via React hooks.
If you need to us the navigate function in a class component you'll need to create your own component that can use the useNavigate hook and pass navigate as a prop to class component.
Example Higher Order Component:
import { useNavigate, /* other hooks */ } from 'react-router-dom';
const withRouter = WrappedComponent => props => {
const navigate = useNavigate();
// other hooks
return (
<WrappedComponent
{...props}
{...{ navigate, /* other hooks */ }}
/>
);
};
export default withRouter;
...
...
import withRouter from '../path/to/withRouter';
export default class RoomJoinPage extends Component {
state = {
roomCode: "",
error: "",
}
handleTextFieldChange = (e) => {
this.setState({
roomCode: e.target.value,
});
}
roomButtonPressed = () => {
const requestOptions = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
code: this.state.roomCode,
}),
};
fetch("/api/join-room", requestOptions)
.then((response) => {
if (response.ok) {
this.props.navigate("/room/" + this.state.roomCode);
} else {
this.setState({ error: "Room not found." });
}
})
.catch((error) => {
console.log(error);
});
}
...
render() { .... }
}
export default withRouter(RoomJoinPage);
...
<Route path="/join" element={<RoomJoinPage />} />
I am using react hooks with redux in my project. In the login component from my action file when I try to redirect to another page i.e. another component. It is redirecting to the login component within a few seconds.
Here is the code:
authReducer.js
const authReducer = (state = iState, action) => {
switch (action.type) {
case "IS_LOGIN":
return {
...state,
isLogin: action.payload,
};
}
})
userAction.js
export const loginSubmit = (data, props) => {
return async (dispatch) => {
axios
.post(`${process.env.REACT_APP_API_URL}login`, data)
.then((result) => {
if (result.data.code == 200) {
dispatch({
type: "IS_LOGIN",
payload: {
data: result.data.data,
authToken: result.data.authToken,
},
});
localStorage.setItem("debateAccountToken", result.data.authToken);
localStorage.setItem("email", result.data.data.email);
localStorage.setItem("id", result.data.data.id);
toast.success("Logged in successfully");
// setInterval(() => {
props.history.push("/log");
// }, 3000);
} else {
toast.error("Email or password wrong!!");
}
})
.catch((err) => {
console.log("error .. ", err);
toast.error("Somethihng went wrong!!");
setInterval(() => {
window.location.reload();
}, 3000);
});
};
};
component file -> route of /log
import React from "react";
function LoginPage() {
return <div>hello</div>;
}
export default LoginPage;
route file
import React, { Component } from "react";
import { BrowserRouter, Route } from "react-router-dom";
import Login from "./components/UserLogin";
import Debate from "./components/debate/Debate";
import LandingPage from "./components/LandingPage";
import UserRegister from "./components/UserRegister";
import LoginPage from "./components/LoginPage";
export default class App extends Component {
render() {
return (
<div>
<BrowserRouter>
<Route exact path="/" component={LandingPage} />
<Route exact path="/register" component={UserRegister} />
<Route exact path="/debate" component={Debate} />
<Route path="/login" component={Login} />
<Route path="/log" component={LoginPage} />
</BrowserRouter>
</div>
);
}
}
From useraction it is redirecting to /log component but eventually it is returning back to login component too. Where might I be mistaken?
I am trying to use history.push method in my redux react app. Its working fine but the problem is my component won't change, do you guys know why?
route.js:
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import { history } from '../helper/history'
export default class route extends React.Component {
render() {
return (
<Provider store={store}>
<Router history={history}>
<Switch>
<Route exact path="/login" component={Login} />
<Route path="/error" component={Error} />
</Switch>
</Router>
</Provider>
)
}
}
Redux action.js where a history is called, this is where I dispatch my actions:
export const loginRequest = () => {
return {
type: userType.LOGIN_REQUEST,
}
}
export const loginSuccess = () => {
return {
type: userType.LOGIN_SUCCESS,
}
}
export const loginFailure = () => {
return {
type: userType.LOGIN_FAILURE,
}
}
export const fetchUser = (data) => {
return (dispatch) => {
dispatch(loginRequest)
axios
.post(env.API_URL + 'login', { email: data.email, password: data.password })
.then((res) => {
dispatch(loginSuccess(res.data.user))
history.push({
pathname: '/profile',
})
})
.catch((err) => {
dispatch(loginFailure(error))
})
}
}
As you are providing history props, you should use Router. See this, Router and BrowserRouter:
import { Router, Route, Switch } from 'react-router-dom'
instead of
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
I have a simple app with auth and private route, I want to get data from the server if the user has token, the back end is ready and works fine, and I log in I have data in redux about the user, but I don't know how to handle with refresh page, where should I do dispatch to call action? if I do it in privateRoute.js, it works strange I want to call server ones, but I did it 3-4 times.
Here are my components without calling sessionActions, sessionActions should update loggedIn and the user would go to /login pages
PrivateRoute
import React, { useState, useEffect } from 'react';
import { Route, Redirect } from 'react-router-dom';
import PropTypes from 'prop-types';
import sessionAction from '../store/actions/sessionAction';
const PrivateRoute = ({ component: Component, ...rest }) => {
const { path, dispatch, loggedIn } = rest;
});
return (
<Route
path={path}
render={(props) => (loggedIn ? <Component {...props} />
<Component {...props}/>
: (<Redirect to="/login" />))}
/>
);
};
PrivateRoute.propTypes = {
component: PropTypes.func.isRequired,
};
export default PrivateRoute;
sessionAction
const sessionAction = (path) => (dispatch) => {
return sessionServices(path)
.then((response) => {
console.log(response);
const { data } = response;
if (!data.error) {
dispatch(success(data));
}
dispatch(failure(data.error.text));
})
.catch((error) => error);
};
sessionService
import axios from 'axios';
axios.defaults.withCredentials = true;
const sessionServices = (path) => axios({
method: 'post',
url: `http://localhost:4000/api/pages${path}`,
})
.then((response) => response)
.catch((error) => console.log(error));
export default sessionServices;
You must dispatch the actions to fetch user data from the server in your App component which is the top-level component. Also, maintain a loading state in the reducer to render a Loader until the user data is fetched.
const App = props => {
useEffect(() {
this.props.sessionActions('/session')
}, []);
if(this.state.isLoading) return <Loader />;
const { loggedIn, user } = this.props;
return (
<Router>
{/* Your Private routes and other routes here */}
</Router>
)
}
const mapStateToProps = (state) => {
return {
isLoading: state.auth.isLoading,
user: state.auth.user,
loggedIn: state.auth.loggedIn
}
}
export default connect(mapStateToProps, { sessionAction })(App);
I want to login user automatically if refresh_token exists in localStorage but my issue here is, i am not able to change state to authenticated initially when app start. I tried with componentWillMount but i am getting old state 1st time then i get updated state. <PrivateRoute> here getting called 2-3 time, don't know where i am making mistake.
Expected Flow
autoLogin
PrivateRoute with new state
App.js
import React, { Component } from 'react'
import WrappedLoginForm from './containers/Login'
import ChatApp from './containers/Chat'
import { connect } from 'react-redux'
import { checkAuthentication } from './store/actions/auth'
import PrivateRoute from './route'
import {
BrowserRouter as Router,
Route
} from "react-router-dom";
class App extends Component {
componentWillMount() {
this.props.autoLogin()
}
render() {
return (
<Router>
<Route path='/login' component={WrappedLoginForm} />
<PrivateRoute path="/" component={ChatApp} authed={this.props.isAuthenticated} />
</Router>
)
}
}
const mapStateToProps = state => {
return {
isAuthenticated: state.isAuthenticated,
loading: state.loading
}
}
const mapDispatchToProps = dispatch => {
return {
autoLogin: () => {
dispatch(checkAuthentication())
}
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(App)
route.js
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
const PrivateRoute = ({ component: Component, authed, ...rest }) => {
console.log('aaa')
return (<Route
render={props => (
authed
? <Component />
: <Redirect to="/login" />
)}
/>)
};
export default PrivateRoute;
action.js
export const checkAuthentication = () => {
return dispatch => {
dispatch(authStart())
let refresh_token = localStorage.getItem('refresh_token')
axiosInstance.post('/api/token/refresh/', {
refresh: refresh_token
}).then(res => {
if (res.status === 200) {
localStorage.setItem("access_token", res.data.access)
dispatch(loginSuccess())
}
}).catch(error => {
console.log("balle")
dispatch(loginFailed(error))
})
}
}
first thing make sure that in your store the isAuthenticated property is false by default.
For PrivateRoute use :
{this.props.isAuthenticated
? <PrivateRoute path="/" component={ChatApp} />
: null
}
instead of <PrivateRoute path="/" component={ChatApp} authed={this.props.isAuthenticated} />