[react]history.push changes url, but not rendering page - reactjs

history.push in SearchInput.js changes url, but it does not re-render the page :(
I read some posts handling this issue, but all does not work.
SearchInput.js component is called at header.
app.js
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import AppContext from '../contexts/AppContext';
import Login from './Login';
import Main from './Main';
import { createBrowserHistory } from "history";
const history = createBrowserHistory();
const App = () => {
return (
<AppContext.Provider value={{}}>
<Router history={history}>
<Switch>
<Route path="/" component={Login} exact/>
<Route path="/contents" component={Main}/>
</Switch>
</Router>
</AppContext.Provider>
)
};
export default App;
main.js
import React, { useState, useEffect } from 'react';
import { Route } from 'react-router-dom';
import _ from "lodash";
import Header from './Share/Header';
import Footer from './Share/Footer';
import Alert from './Share/Alert';
import AlertContext from '../contexts/AlertContext';
import ShareDataContext from '../contexts/ShareDataContext';
import Search from './Search/Search';
import Targeting from './Targeting/Targeting';
import ModelHit from './ModelHit/ModelHit';
import {KEY_NAME} from '../consts/keyName';
const Main = (props) => {
const [alertState, setAlertState] = useState({
eventType: '',
eventMessage: '',
eventCount: 0,
});
return (
<AlertContext.Provider value={{ alertState, setAlertState }}>
<Header quarterRawData={quarterRawData}/>
<Alert />
<main className="blue-grey lighten-5">
<Route path="/contents/search" component={Search} exact />
<Route path="/contents/targeting" component={Targeting} exact />
<Route path="/contents/model-hit-list" component={ModelHit} exact />
</main>
<Footer />
</AlertContext.Provider>
);
};
export default Main;
SearchInput.js -> where call history.push
/* eslint-disable no-use-before-define */
import React from 'react';
import TextField from '#material-ui/core/TextField';
import Autocomplete from '#material-ui/lab/Autocomplete';
import {KEY_NAME} from '../../consts/keyName'
import { useHistory } from 'react-router-dom';
const SearchInput = (props) => {
// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top
const {options} = props;
const history = useHistory();
const searchHandler = (e, value) => {
if (value) {
history.push({
pathname: '/contents/search',
state: {
[KEY_NAME.SHARE_CODE]: value[KEY_NAME.SHARE_CODE],
},
})
}
}
return (
<Autocomplete
id="combo-box-demo"
options={options}
getOptionLabel={(option) => option['target']}
style={{ width: 400 }}
onChange={searchHandler}
renderInput={(params) => <TextField {...params} label="Search" variant="outlined"/>}
/>
);
}
export default SearchInput;

Related

UseContext React: Data doesn't transfer from Login component to SideBar component

I'm trying to use a context in my react project but i dont know why the component "SideBar" can't update the context. The user flow is a user and passwrod simple form in the Login component and if the credentials are valid i want to display the username in the sidebar of my dashboard, as you see this is nothing fancy.
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { UserProvider } from './contexts/user.context';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<BrowserRouter>
<UserProvider>
<App />
</UserProvider>
</BrowserRouter>
</React.StrictMode>
);
reportWebVitals();
App.jsx
import './App.css';
import { Routes, Route } from 'react-router-dom';
import Home from './routes/home/Home.component';
import Login from './routes/auth/Login.component';
import Dashboard from './components/Usuarios/UsuarioValid/MainColumn/Dashboard.component';
import Infrastructura from './components/Usuarios/Anonimo/MainColumn/Infrastructura.component';
import EquipoDirectivo from './components/Usuarios/Anonimo/MainColumn/EquipoDirectivo.component';
import AsignaturasRamas from './components/Usuarios/Anonimo/MainColumn/AsignaturasRamas.component';
const App = () => {
return (
<Routes>
<Route path="/" element={<Login />} />
<Route path="anonimo" element={<Home isAnonimo />} >
<Route index element={<Infrastructura />} />
<Route path="equipo" element={<EquipoDirectivo/>} />
<Route path="asignaturas" element={<AsignaturasRamas/>} />
</Route>
<Route path="dashboard" element={<Home isAnonimo={false} />} >
<Route index element={<Dashboard />} />
</Route>
</Routes>
)
}
export default App;
Home.component.jsx
import React from 'react'
import { Outlet } from 'react-router-dom';
import Sidebar from '../../components/Usuarios/UsuarioValid/Sidebar.component';
import TransitionRoot from '../../components/Usuarios/UsuarioValid/TransitionRoot.component';
import SidebarAnonimo from '../../components/Usuarios/Anonimo/SidebarAnonimo.component';
import TransitionRootAnonimo from '../../components/Usuarios/Anonimo/TransitionRootAnonimo.component';
const Home = (props) => {
const { isAnonimo } = props;
return (
<>
{
isAnonimo ? (
<div>
<TransitionRootAnonimo />
<SidebarAnonimo />
<Outlet />
</div>
)
: (
<div>
<TransitionRoot />
<Sidebar />
<Outlet />
</div>
)}
</>
)
}
export default Home;
user.context.jsx
import { createContext, useState} from "react";
// Actual value we want to access
export const UserContext = createContext({
currentUser: null,
setCurrentUser: (x) => x,
});
// Used in route file (index.js) to handle context
export const UserProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState(null);
const value = { currentUser, setCurrentUser };
return <UserContext.Provider value={value}> {children} </UserContext.Provider>
}
login.component.jsx
import { useState, useContext } from "react";
import { UserContext } from '../../contexts/user.context';
const Login = () => {
const { currentUser, setCurrentUser } = useContext(UserContext)
const [ formFields, setFormFields ] = useState(defaultFormFields);
const { usuario, password } = formFields;
const [ validCredentials, setValidCredentials ] = useState(true);
const handleSubmit = async (event) => {
event.preventDefault();
const usuariValid = usuarios.find(u =>
u.usuario === usuario
&& u.contraseƱa === password
);
if(usuariValid){
setCurrentUser(usuariValid);
console.log(currentUser)
/* window.location.replace('/dashboard'); */
} else {
resetFormFields();
setValidCredentials(false);
console.info("Credenciales invalidas");
}
};
/*NOT RELEVANT CODE*/
sidebar.component.jsx
*
import { UserContext } from '../../../contexts/user.context'
import React, {useState, useEffect, useContext, Fragment} from 'react'
const Sidebar = () => {
const { currentUser } = useContext(UserContext);
const [idAsignaturas, setIdAsignaturas] = useState([]);
const [nombreAsignaturas, setNombreAsignaturas] = useState([])
useEffect(()=>{ console.log(currentUser},[currentUser]) /*OUTPUT:null*/
/*NOT RELEVANT CODE*/
}*
The code is correct, the problem is that the Context in react is useful for components that are mounted at the same time. In our case we were trying to use the context in components that are not mounted at the same time, because the component "Sidebar" is loaded after the user logs in "Login". So when the user logs in, the pages are reloaded and "Sidebar" is mounted, when this happens the context restarts and loses the values that had been updated in the "Login" component.

I am haveing problem when i am try to change my class component in functional component in react js

app.js file code i want to convert it
import Toolbar from "./components/Toolbar";
// import Iqac from "./components/Iqac/iqac";
import React from "react";
import "./styles.css";
import SideDrawer from "./components/sidemenu/SideDrawer";
import BackDrop from "./components/backdrop/BackDrop";
import { Route, Router, Routes } from "react-router-dom";
class App extends React.Component {
state = {
};
drawerToggleClickHandler = () => {
this.setState(prevState => {
return { sideDrawerOpen: !prevState.sideDrawerOpen };
});
};
backDropClickHandler = () => {
this.setState({ sideDrawerOpen: false });
};
render() {
// let sideDrawer;
let backdrop;
if (this.state.sideDrawerOpen) {
// sideDrawer = <SideDrawer />;
backdrop = <BackDrop click={this.backDropClickHandler} />;
}
return (<>
<div className="tool">
<Toolbar drawerToggleClickHandler={this.drawerToggleClickHandler} />
<SideDrawer show={this.state.sideDrawerOpen} />
{backdrop}
</div>
</>
);
}
}
export default App;
i have tried to change also i want to impliment react router dom v6 so that is why is need to do this and i don't know how to do in react-class based component
import Toolbar from './components/Toolbar'
import Footer from './components/Footer'
import Home from './Pages/Home'
import { useState } from "react";
import About from "./Pages/About"
import {
BrowserRouter as Router,
Route,
Routes
} from "react-router-dom";
const App = () => {
const [state, setstate] = useState({
sideDrawerOpen: false
});
return (
<>
<Router>
<Toolbar />
<Routes>
<Route path='/' element={<Home />}> </Route>
<Route path='/about/*' element={<About />}> </Route>
</Routes>
</Router>
<Footer />
</>
);
}
export default App;
i want to use react router dom and i thing v6 does't work with class
component
please help me resolve this issue i need to lot of your support thankyou so much in advance for the help
Try to change your code like this:
import Toolbar from './components/Toolbar';
import Footer from './components/Footer';
import Home from './Pages/Home';
import { useState } from 'react';
import About from './Pages/About';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
const App = () => {
const [sideDrawerOpen, setSideDrawerOpen] = useState(false);
const drawerToggleClickHandler = () => {
setSideDrawerOpen((prevState) => !prevState);
};
const backDropClickHandler = () => {
setSideDrawerOpen(false);
};
return (
<div className='tool'>
<Toolbar drawerToggleClickHandler={drawerToggleClickHandler} />
<SideDrawer show={sideDrawerOpen} />
{sideDrawerOpen && <BackDrop click={backDropClickHandler} />}
<Router>
<Toolbar />
<Routes>
<Route path='/' element={<Home />}>
{' '}
</Route>
<Route path='/about/*' element={<About />}>
{' '}
</Route>
</Routes>
</Router>
<Footer />
</div>
);
};
export default App;

Best practise: Where to put the Navbar component so that it -re-renders with every page?

This has been bugging me for a long time, and my head just can't seem to come up with a solution that works fluently. I will try to be short:
I have a React app where I update my Navbar based on an access token being stored as a cookie or not (user authenticated or not). But in order for my Navbar to update whether the user is authenticated, I have to refresh the page. I believe it is because my Navbar is being rendered way up (in?) the component tree, and does not get re-rendered whenever I visit a new page (only when I refresh).
What is the best way to render my Navbar component so that every page shares the same component, BUT the Navbar re-renders on every new page visited? Or is this even a wise thing to do?
Let me know if I am not explaining myself clearly.
index.jsx
import React from "react";
import ReactDOM from "react-dom";
import { App } from "./components/App";
import { BrowserRouter } from "react-router-dom";
import "./styles/styles.css";
import { PublicClientApplication } from "#azure/msal-browser";
import { MsalProvider } from "#azure/msal-react";
import { msalConfig } from "./lib/authConfig";
const msalInstance = new PublicClientApplication(msalConfig);
ReactDOM.render(
<MsalProvider instance={msalInstance}>
<BrowserRouter>
<App />
</BrowserRouter>
</MsalProvider>,
document.getElementById("root")
);
App.jsx
import React from "react";
import { Router } from "./Router";
import { Navbar } from "./Navbar";
export function App() {
return (
<div>
<Navbar />
<Router />
</div>
);
}
Router.jsx
import { Route } from "react-router-dom";
import React from "react";
import { MainPage } from "./MainPage";
import { LoginPage } from "./LoginPage";
import { GoogleLogin } from "./GoogleLogin";
import { GoogleLoginCallback } from "./GoogleLoginCallback";
import { GoogleLogout } from "./GoogleLogout";
import { NewArticle } from "./NewArticle";
export function Router() {
return (
<div>
<Route exact path="/" component={MainPage} />
<Route exact path="/login" component={LoginPage} />
<Route exact path="/login/google" component={GoogleLogin} />
<Route exact path="/logout/google" component={GoogleLogout} />
<Route exact path="/newArticle" component={NewArticle} />
<Route
exact
path="/login/google/callback"
component={GoogleLoginCallback}
/>
</div>
);
}
Navbar.jsx
import React, { useEffect, useState } from "react";
import { NavLink } from "react-router-dom";
import { useIsAuthenticated, useMsal } from "#azure/msal-react";
import { fetchJSON } from "../lib/fetchJSON";
export function Navbar() {
const activeStyle = {
borderBottom: "10px solid white",
};
const [username, setUsername] = useState();
const { accounts } = useMsal();
const name = accounts[0] && accounts[0].name;
const hasCookie = document.cookie.startsWith("access_token");
const isAuthenticated = useIsAuthenticated();
useEffect(async () => {
const res = await fetchJSON("/api/login/google");
if (hasCookie) {
setUsername(res.userinfo.given_name);
}
}, []);
return (
<>
<div className={"navbar-container"}>
<NavLink exact to="/" activeStyle={activeStyle}>
Home
</NavLink>
<NavLink exact to="/login" activeStyle={activeStyle}>
Login-portal
</NavLink>
{isAuthenticated ? (
<NavLink exact to="/newArticle" activeStyle={activeStyle}>
New Article
</NavLink>
) : (
<div />
)}
{isAuthenticated || hasCookie ? (
<p className="logged-in">
Logged in as {username} {name}
</p>
) : (
<p className="logged-in">Not logged in</p>
)}
</div>
</>
);
}

Try to navigate users to different pages by their roles using react router

So after user is logged in, getProfile() is called to get their profile information.
and by their role, users will be navigated to user page or admin page.
In order to do that, I made ProtectedRoute component.
But each user page and admin page are not appeared when I use this component.(only white screen is shown.)
I've checked the getProfile() in ProtectedRoute component is receiving result well.
but it seems like <Layout {...props} /> or <AdminLayout {...props} /> is not rendered.
Here's the codes.
protectedRoute.js : Filter users by their roles and navigate them to proper page.
import React, {useEffect} from 'react';
import {BrowserRouter as Router, Switch, Route, Redirect} from 'react-router-dom';
import Layout from './layouts/layout';
import AdminLayout from './layouts/adminLayout';
import {useDispatch} from 'react-redux';
import {getProfile} from './data_actions/userInfo_action/userInfo_action';
import {unwrapResult} from '#reduxjs/toolkit';
import {useHistory} from 'react-router-dom';
export const ProtectedRoute = ({component: Component, admin = null, ...rest}) => {
const dispatch = useDispatch();
return (
<Route
{...rest}
path={rest.path}
render={(props) => {
dispatch(getProfile())
.then(unwrapResult)
.then((res) => {
if ((res.role === 'PRO' || res.role === 'CLIENT') && !admin) {
<Layout {...props} />;
} else if (res.role === 'ADMIN' && admin) {
<AdminLayout {...props} />;
}
})
.catch((err) => {
return (
<Redirect
to={{
pathname: '/',
state: {
from: props.location,
},
}}
/>
);
});
}}
/>
);
};
App.jsx : Where protectedRoute component is placed.
import React, {useContext, useEffect, useRef, useState} from 'react';
import Layout from './layouts/layout';
import AdminLayout from './layouts/adminLayout';
import './App.scss';
import {BrowserRouter as Router, Switch, Route, Redirect} from 'react-router-dom';
import LoginPage from '#/pages/user/sign/login';
import NotFound from './pages/notFound';
import {ProtectedRoute} from './protectedRoute';
function App() {
return (
<Router>
<Switch>
{/* login */}
<Route exact path={`/fn/sign/login`} render={(props) => <LoginPage {...props} />} />
<ProtectedRoute exact path={`${process.env.PUBLIC_URL}`} component={Layout} admin={false} />
<ProtectedRoute
exact
path={`${process.env.PUBLIC_URL}/admin`}
component={AdminLayout}
admin={true}
/>
<Route path="*" component={NotFound} />
</Switch>
</Router>
);
}
export default App;
layout.jsx : Layout Component that contains user's router.
import React, {memo, useContext, useEffect} from 'react';
import PropTypes from 'prop-types';
import Drawer from '#material-ui/core/Drawer';
import Hidden from '#material-ui/core/Hidden';
import {makeStyles, useTheme} from '#material-ui/core/styles';
import SideBar from './sidebar';
import './layout.scss';
import Router from '../routes/router';
const useStyles = makeStyles((theme) => ({
root: {
display: 'flex',
},
toolbar: {
[theme.breakpoints.up(theme.breakpoints.values.md)]: {
display: 'none',
},
},
content: {
width: '100%',
flexGrow: 1,
},
}));
function Layout(props) {
const classes = useStyles();
return (
<div className={classes.root}>
<nav className={classes.drawer}>
<Hidden mdDown implementation="css">
<Drawer
className={classes.drawer}
classes={{
paper: classes.drawerPaper,
}}
variant="permanent"
open
>
<SideBar />
</Drawer>
</Hidden>
</nav>
<main className={classes.content}>
<div className={classes.toolbar} />
<Router {...props} />
</main>
</div>
);
}
Layout.propTypes = {
window: PropTypes.func,
};
export default memo(Layout);
router.js : user's pages
import React, {useEffect} from 'react';
import {Switch, Route, Redirect} from 'react-router-dom';
import JobList from '#/pages/user/jobNotiBoard/jobList';
import JobDetail from '#/pages/user/jobNotiBoard/jobDetail';
import NotFound from '#/pages/notFound';
import {useDispatch, useSelector} from 'react-redux';
import {setNewCount2} from '#/data_actions/jobNoti_action/newDataCount_action';
function Router(props) {
const dispatch = useDispatch();
const newDataCount = useSelector((state) => state.newJobNotiPostCount);
useEffect(() => {
dispatch(setNewCount2());
}, []);
return (
<React.Fragment>
<Switch>
<Route
exact
path={`${process.env.PUBLIC_URL}/joblist`}
render={(props) => <JobList {...props} newDataCount={newDataCount?.count} />}
/>
<Route
path={`${process.env.PUBLIC_URL}/detail/:id/:site`}
render={(props) => <JobDetail {...props} />}
/>
<Route path="*" component={NotFound} />
</Switch>
</React.Fragment>
);
}
export default Router;
The render prop in your ProtectedRoute does not return anything.
You will have to change it to something like
export const ProtectedRoute = ({
component: Component,
admin = null,
...rest
}) => {
const dispatch = useDispatch();
const [role, setRole] = React.useState(null);
React.useEffect(
() => {
dispatch(getProfile())
.then(unwrapResult)
.then((res) => {
setRole(res.role);
})
.catch((err) => {
setRole("PROFILE_ERROR");
});
},
[ /* dependencies for when to re-request the profile*/ ]
);
return (
<Route
{...rest}
path={rest.path}
render={(props) => {
if ((role === "PRO" || role === "CLIENT") && !admin) {
return <Layout {...props} />;
} else if (role === "ADMIN" && admin) {
return <AdminLayout {...props} />;
} else if (role === "PROFILE_ERROR") {
return (
<Redirect
to={{
pathname: "/",
state: {
from: props.location
}
}}
/>
);
}
}}
/>
);
};

props.history.push() doesn't redirect

props.history.push("/") doesn't redirect.
I did search for solution for this problem and can't find where is the problem and that drives me crazy.
index.js
import 'bootstrap/dist/css/bootstrap.css';
import 'bootstrap/dist/css/bootstrap-theme.css';
import './index.css';
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'react-router-redux';
import { createBrowserHistory } from 'history';
import configureStore from './store/configureStore';
import App from './App';
//import registerServiceWorker from './registerServiceWorker';
// Create browser history to use in the Redux store
const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href');
const history = createBrowserHistory({ basename: baseUrl });
// Get the application-wide store instance, prepopulating with state from the server where available.
const initialState = window.initialReduxState;
const store = configureStore(history, initialState);
//Render app on "root" <div> in index.html
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}>
<App />
</ConnectedRouter>
</Provider>,
document.getElementById('root'));
//registerServiceWorker();
app.js
import React from 'react';
import { Route, Switch } from 'react-router';
import Home from './components/Home';
import Login from './components/Login/Login';
import Counter from './components/Counter';
import FetchData from './components/FetchData';
import { PrivateRoute } from './components/PrivateRoutes/PrivateRoute';
const App = (props) => {
return (
<Switch>
<Route path="/login" component={Login}/>
<PrivateRoute path="/" component={Home} />
<Route path='/counter' component={Counter} />
<Route path='/fetchdata/:startDateIndex?' component={FetchData} />
</Switch>
);
}
export default App;
Login.js
import React, { useState } from 'react';
import { withRouter, Redirect, history } from 'react-router-dom'
import { Form, Label, FormGroup, FormControl, Button } from 'react-bootstrap';
import Home from '../Home';
//Login user
function LoginUser(username, password, callback) {
console.log("Atemt to login..." + " " + username);
fetch('api/SampleData/Login', {
method: "POST",
body: JSON.stringify({
email: username,
password: password,
})
}).then(response => response.json())
.then(json =>callback(json))
}
function Login(props) {
var logged = false;
var data = { username: '', password: '', };
function getUsername(event) {
data.username = event.target.value;
console.log(data);
}
function getPassword(event) {
data.password = event.target.value;
console.log(data);
}
function requestCallback(res) {
if (res[0] === "connected") {
props.history.push('/');
console.log(props.history);
}
}
if (logged === true) {
return (<Redirect to="/" component={Home} />);
}
return (
<div style={{ position: 'absolute', left: '50%', top: '50%', transform: 'translate(-50%, -50%)' }}>
<Form >
<FormGroup controlId="formBasicEmail">
<Label>Email address</Label>
<FormControl type="email" placeholder="Enter email" onChange={getUsername} />
</FormGroup>
<FormGroup controlId="formBasicPassword">
<Label>Password</Label>
<FormControl type="password" placeholder="Password" onChange={getPassword} />
</FormGroup>
<Button variant="primary" onClick={() => LoginUser(data.username, data.password, requestCallback)} style={{ margin: '0 auto', display: 'block', width: '100px' }}>
Login
</Button>
</Form>
</div>
);
}
export default withRouter(Login);
As you can see Login component is wrapped with withRouter(Login). console.log(props) in Login.js file shows that history is passed to props.
The problem is that in your PrivateRoute you have a constant variable called userLogged which is initiated with the value false. If the variable is true, the defined component is rendered. If it is false, you redirect ot /login.
The value of userLogged is always false, so you always redirect to /login. I recommend you handle your logged-in state either in your parent component App or through the usage of a store from redux.
Use 'history' npm package
1)App.js
import React, { Component } from "react";
import { Route, Router } from "react-router-dom";
import { createBrowserHistory } from "history";
import Dashboard from "./components/dashboard.js ";
import Login from "./components/login.js";
import Profile from "./components/profile.js";
import PrivateRoute from "./privateRoute.js";
export const history = createBrowserHistory();
//refer 'history' for wanted component like profile.js
class App extends Component {
render() {
return (
<Router history={history}>
<div>
<PrivateRoute
path="/"
component={Dashboard}
exact
/>
<Route path="/login" component={Login} exact />
<PrivateRoute path="/profile" component={Profile} />
</div>
</Router>
);
}
}
export default App;
a)Once you login store some data in local Storage with key "user".
b)Based on this "user" object in localStorage routing will happen in prBivateRoute.js
c)If you want to log out clear localStorage it will navigate into loginPage
2)privateRoute.js
import React from "react";
import { Route, Redirect } from "react-router-dom";
const PrivateRoute = ({ component: Component, ...rest }) => {
return (
<Route
{...rest}
render={props => {
if (localStorage.getItem("user")!= "") {
return <Component />;
}else {
return <Redirect to={{ pathname: "/login" }} />;
}
}}
/>
);
};
3)profile.js
import React, { Component } from "react";
import { history } from "./App.js";
class Profile extends Component {
goBack = () => {
history.push("/");
};
render() {
<button onClick={() => this.goBack()}> back to dashboard </button>;
}
}
export default Profile;

Resources