history.push() is updating urls but not re-rendering components - reactjs

I'm facing a problem with react-router-dom. I'm trying to use history.push for navigating after an action conducted. but the problem is createBrowserHistory from history is updating the urls but components are not re-rendering. I've used every solution from https://stackoverflow.com/. But it's still not working as expected.
However I found a reason behind it. As my components are wrapped with connect function connect is preventing the re-render. And there was a solution too, wrap the connect function with withRouter. I tried it too. But it's not working.
Here is My App.js
import React, { Component } from "react";
import { Router, Route } from "react-router-dom";
import history from "../history"
import Navbar from "./Navbar";
import LogIn from "./LogIn";
import StreamCreate from "./streams/StreamCreate";
import StreamDelete from "./streams/StreamDelete";
import StreamEdit from "./streams/StreamEdit";
import StreamList from "./streams/StreamList";
import StreamShow from "./streams/StreamShow";
import Profile from "./streams/Profile";
class App extends Component {
render() {
return (
<div>
<Router history={history}>
<div>
<Navbar />
<Route path="/" exact component={StreamList} />
<Route path="/streams/new" exact component={StreamCreate} />
<Route path="/streams/delete" exact component={StreamDelete} />
<Route path="/streams/edit" exact component={StreamEdit} />
<Route path="/streams/show" exact component={StreamShow} />
<Route path="/login" exact component={LogIn} />
<Route path="/my-streams" exact component={Profile} />
</div>
</Router>
</div>
);
}
}
export default App;
Here is the history.js
import { createBrowserHistory } from 'history';
export default createBrowserHistory();
Action Creator:
import Streams from "../API/Streams";
import history from "../history";
export const createStreams = (formData) => async (dispatch, getState) => {
const { userId } = getState().auth;
const response = await Streams.post("/streams", { ...formData, userId });
dispatch({ type: "CREATE_STREAM", payload: response.data });
history.push("/")
};

Related

How to send the last value of State as a property

I just learn react and this is the problem I haven't been able to figure out for hours.
I have the App component and I want to pass data about user login to the ProtectedRoute component so I can handle access to the admin page.
In the App there is a function which manages lifting data up from the Login component (based on Firebase user authentication).
The problem is, the userLogged state is set too late so Protected route receives default value of the state.
How can I send the last state to the ProtectedRoute component?
import {useState} from "react"
import './App.css'
import Login from "./components/admin/Login"
import Homepage from "./components/homepage/Homepage"
import { BrowserRouter, Routes, Route, Link } from "react-router-dom";
import ProtectedRoute from "./ProtectedRoute"
import AdminPage from "./components/admin/AdminPage"
function App() {
const [userLogged, setUserLogged] = useState()
const getUser = (data) => {
setUserLogged(data)
console.log("data: " + data)
console.log("state: " + userLogged)
}
return (
<>
<Routes>
<Route path="/" element={<Homepage />} />
<Route path="/login" element={<Login sendUser={getUser}/>} />
<Route element={<ProtectedRoute isLoggedIn={userLogged} />}>
<Route path="/admin" element={<AdminPage />} />
</Route>
</Routes>
</>
);
}
export default App
This is the ProtectedRoute component, where I tried to add useEffect. It still doesn't work this way.
import { Navigate, Outlet } from "react-router-dom"
import {useState, useEffect} from "react"
const ProtectedRoute = (props) => {
const [isLogged, setIsLogged] = useState("")
useEffect(() => {
setIsLogged(props.isLoggedIn);
console.log(isLogged);
}, [props.isLoggedIn]);
return isLogged ? <Outlet /> : <Navigate to="/" />;
};
export default ProtectedRoute;

react router history.push not loading expected component

I am using saga for managing my async call.
Here is login saga :
export function* loginApi({ type, formObj }) {
const formData = new FormData();
Object.keys(formObj).map(key => {
formData.append(key, formObj[key]);
})
const response = yield axios.post(`http://localhost/coupon/web/api/login`, formData)
if (response.data.status) {
localStorage.setItem('token', response.data.data.token);
yield put(loginsuccess(response.data.data.token))
yield call(forwardTo, '/dashboard')
} else {
yield put(loginFaield(response.data.msg))
}
}
After login successful I want redirect user to dashboard so for that:
yield call(forwardTo, '/dashboard') which execute forwardTo function and below is implementation of it:
import { createBrowserHistory } from 'history';
const history = createBrowserHistory();
function forwardTo(location) {
console.log('location', location);
history.push(location);
}
export default forwardTo;
It change my url from http://localhost:3000/login to http://localhost:3000/dashboard but it shows login page instead of dashboard.
Routes :
const Routes = () => {
return (
<Switch>
<Route path="/register" component={RegisterUser} />
<Route path="/dashboard" component={Dashboard} />
<Route path="/login" component={Login} />
<Route path="/" component={Home} />
</Switch>
)
}
You can achieve this in the Login Component...
After the saga does this yield put(loginsuccess(response.data.data.token))
Now I assume.. some data regarding login successful is stored. Say state.user.token is null before login, after login it has some value.
Do change your Login Component to look like this.
import React from 'react';
import {Redirect} from "react-router-dom";
import {withRouter} from "react-router-dom";
import {compose} from 'redux';
import {connect} from 'react-redux';
const Login = props => {
return (
<div>
/*all login UI */
//..
{
props.token ?
<Redirect to={'/dashboard'}/> : null
}
</div>
)
};
const mapStateToProps = state => ({
token: //state.user.token or whatever
});
export default compose(
withRouter,
connect(mapStateToProps, mapDispatchToProps)
)(Login);
When user is not logged in the token value is null so it renders nothing but when login is successful, some value assigned to token, as the result <Redirect to='/dashboard'/> will be rendered and the site will be redirected to /dashboard
You're creating new history each time you invoke createHistory(), try to add this function to the app so that it's fired once the app is running and add exact property for all your routes
<Switch>
<Route exact path="/register" component={RegisterUser} />
<Route exact path="/dashboard" component={Dashboard} />
<Route exact path="/login" component={Login} />
<Route exact path="/" component={Home} />
</Switch>
I have solved this problem a few days ago.
I think you have created Router with BrowserRouter.
If so, you should change it to default Router and add history.
Do it like below code.
history.tsx
import { createBrowserHistory } from 'history';
export default createBrowserHistory();
store.tsx
import { createBrowserHistory } from 'history';
const history = createBrowserHistory();
export default history;
// You can add middleware here...
App.tsx
import { FC } from "react";
import { Router, Switch, Route } from "react-router-dom";
import history from '../utils/history';
interface FuncProp {}
const AppRouter: FC<FuncProp> = () => {
return (
<Router history={history}>
<Switch>
<Route path="/home">
<p>HomePage</p>
</Route>
<Route path="*">
<p>Page not found. (404)</p>
</Route>
</Switch>
</Router>
);
};
export default AppRouter;

Rect Js router stop when routes import from external file

i working by react js router (react-router-dom v4.2) and Redux together and have strange varios problem
first i say i successfully used and run this but the problems
when i use the root route as first rote in route list router stop working!!
but when i use '/' route as last route everything is ok
<Switch>
<Route path="/blog" component={Blog} />
<Route path="/users/" component={Users} />
<Route path="/" component={Home} axact />
</Switch>
when i create routes as external component and import it to app routes not work
this is my route.js codes
import React, { Component } from 'react';
import { Switch, Route } from "react-router-dom";
// import Components
import Home from './components/Home'
import Blog from './components/blog/Blog'
import Users from './components/users/Users'
class router extends Component {
render() {
return (
<div>
<Switch>
<Route path="/" component={Home} axact />
<Route path="/blog" component={Blog} />
<Route path="/users/" component={Users} />
</Switch>
</div>
);
}
}
export default router;
this is my App.js codes:
import React, { Component } from 'react';
import { getUsers } from './store/actions'
import { connect } from 'react-redux'
import { withRouter } from "react-router-dom";
import Nav from './components/navigation/Nav';
import axios from 'axios';
const userApi = "https://jsonplaceholder.typicode.com/users"
class App extends Component {
componentWillMount() {
axios.get(userApi)
.then(response => {
let users = response.data
this.props.getUserList(users)
})
.catch(error => {
console.log(error)
})
}
render() {
return (
<div className="container">
<Nav />
<Router />
</div>
);
}
}
const mapDispatchToProps = dispatch => ({ getUserList: (api) => dispatch(getUsers(api)) })
export default withRouter(connect(null, mapDispatchToProps)(App));
when i use this codes everything is ok
import React, { Component } from 'react';
import { getUsers } from './store/actions'
import { connect } from 'react-redux'
import { Switch, Route, withRouter } from "react-router-dom";
import Home from './components/Home'
import Blog from './components/blog/Blog'
import Users from './components/users/Users'
import Nav from './components/navigation/Nav';
import axios from 'axios';
const userApi = "https://jsonplaceholder.typicode.com/users"
class App extends Component {
componentWillMount() {
axios.get(userApi)
.then(response => {
let users = response.data
this.props.getUserList(users)
})
.catch(error => {
console.log(error)
})
}
render() {
return (
<div className="container">
<Nav />
<Switch>
<Route path="/blog" component={Blog} />
<Route path="/users/" component={Users} />
<Route path="/" component={Home} axact />
</Switch>
</div>
);
}
}
const mapDispatchToProps = dispatch => ({ getUserList: (api) => dispatch(getUsers(api)) })
export default withRouter(connect(null, mapDispatchToProps)(App));

How to add Nested and Non-Nested Routes with React Router - ReactJS?

I have a Router File, where my Routes are nested under Index Component.
However, I want some other component, login which I don't want to Nest under any component but I want to use it to redirect to '/' home route.
If I use Div tags, then it is messing with my Template.
I am adding the Login component - route inside the Switch Tag.
If I don't do that I get React can only have one child error.
Does anyone know how to do a Nested Route and a Non-Nested One? Please Help.
This is my Router File.
import React, { Component } from 'react';
import './App.css';
import { Provider } from 'react-redux';
import store from './store/store';
import { Router, Route , Switch } from 'react-router-dom';
import Index from './actions/indexToggle/indexActions';
import FirstDashboard from './_layouts/views/firstDashboard';
import SecondDashboard from './_layouts/views/secondDashboard';
import ThirdDashboard from './_layouts/views/thirdDashboard';
import FourthDashboard from './_layouts/views/fourthDashboard';
import history from './history';
import FifthDashboard from './_layouts/views/fifthDashboard';
import Login from './_layouts/views/Login/login';
const Main = () => (
<Provider store={store}>
<Router history={history}>
<Switch>
<Index>
<Route exact path='/overview1' component={FirstDashboard} />
<Route exact path='/overview2' render={(props) => <SecondDashboard {...props} show="show" /> } />
<Route exact path='/overview3' component={ThirdDashboard} />
<Route exact path='/overview4' component={FourthDashboard} />
<Route exact path='/overview5' component={FifthDashboard} />
</Index>
<Route path='/login' component={Login} />
</Switch>
</Router>
</Provider>
)
export default Main;
Here what I've done. See DEMO.
I don't wanna be too confused because of this, so I choose a simple way.
routes.js
import Home from "./pages/Home";
import ComplexPath from "./pages/ComplexPath";
import Login from "./pages/Login";
export default [
{
path: "/",
component: Home,
withHeaderSidenav: true
},
{
path: "/yet/another/complex/path",
component: ComplexPath,
withHeaderSidenav: true
},
{
path: "/login",
component: Login,
withHeaderSidenav: false
}
];
Then, simply map the routes.
App.js
import React from "react";
import { Switch, Route } from "react-router-dom";
import BaseLayout from "./BaseLayout";
import routes from "./routes";
export default class extends React.Component {
state = {
withHeaderSidenav: true
};
showHeaderSidenav = (withHeaderSidenav = true) => {
this.setState({ withHeaderSidenav });
};
render() {
return (
<BaseLayout withHeaderSidenav={this.state.withHeaderSidenav}>
<Switch>
{routes.map(route => (
<Route
exact
key={route.path}
path={route.path}
render={() => (
<route.component
showHeaderSidenav={() =>
this.showHeaderSidenav(route.withHeaderSidenav)
}
/>
)}
/>
))}
</Switch>
</BaseLayout>
);
}
}
There will be a HOC for each page to handle layout changing. See pages/withBase.js in demo project.

React Route V4 history has pushed but the page was not redirect

I try to use React Route V4 to build the route of app.
as following:
import createHistory from 'history/createBrowserHistory';
const history = createHistory();
<Provider store= {store}>
<MuiThemeProvider>
<Router history={history}>
<div>
<LayoutContainer/>
<Route exact path="/" component={Home} />
<Route path="/records" render={() =>{
return (<DealsContainer/>)
}}/>
<Route path="/sign_in" component={SignInContainer}/>
</div>
</Router>
</MuiThemeProvider>
</Provider>
I compared the method this.props.history.push('/') and history.push("/"), I find their is different way when I put it to different lifecycle method.
//dealsContainer.js
import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import history from '../_helpers/history';
componentDidMount(){
//history.push("/sign_in"); //browser url will change but no redirect
//this.props.history.push("/sign_in"); //it work
}
render(){
if(this.props.authenticate.LoggedIn == false){
//this.props.history.push("/sign_in"); // it work
//history.push("/sign_in"); // it work
}
const mapStateToProps = (state) => {
return{
deals: state.deals,
authenticate: state.authenticate,
}
};
export default withRouter(connect(mapStateToProps)(DealsContainer))
And the history.push("/") in the asyncActions function was not working.
Does it possible to let it work in asyncActions?

Resources