I am currently stuck without getting errors, and I can not figure out what to try next.
I have an App.js which contains the standard page switching, which works:
function App() {
return (
<BrowserRouter>
<nav>
<Link to="/googlesearch">
<i className="material-icons">search</i>
</Link>
<Link to="/createUser">
<i className="material-icons">add</i>
</Link>
<Link to="/">
<i className="material-icons">home</i>
</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/create" element={<Create />} />
<Route path="/createUser" element={<CreateUser />} />
<Route path="/googlesearch" element={<GoogleSearch />} />
<Route path="/:id" element={<Update />} />
</Routes>
</BrowserRouter>
);
}
export default App;
When I click my googlesearch, I correctly move to my google search page:
function GoogleSearch() {
return (
<div className="main-wrapper">
<MyGoogleMap />
</div>
);
}
export default GoogleSearch
Which renders my google maps component.
Here, I have the integrations figured out, and when I make my location search using the Google DistanceMatrixService, I trigger the callback, confirmed by successful console.logs.
Currently, it looks like this:
import { Navigate } from "react-router-dom";
...
...
Render() {
return(
<Wrapper>
...
<div>
<DistanceMatrixService
options={{
origins: [autocompletedplace['formatted_address']],
destinations: destinations,
travelMode: "DRIVING",
}}
callback = {(response) => { return(<Navigate to="/" />)}}
/>
</div>
</Wrapper>
);
}
I have tried Navigate to "/pages/Home", just for a full path attempt.
The current path I am trying to navigate away from is: "/pages/GoogleSearch"
I am currently getting no errors in the console, but my page does not change.
So, how do I change the page to a different one, and pass destinations and my response to this new page?
Calling Navigate only works inside JSX. In non-JSX parts, the idea is to use useNavigate, but it only works inside a functional component. Since your component is a class component, you can do this:
import React, { Component } from "react";
import { Navigate } from "react-router-dom";
export default class ClassComponentName extends Component {
constructor(props) {
super(props);
this.state = {
// others states
redirectToHome: false,
};
}
render() {
if (this.state.redirectToHome) {
return <Navigate to="/" />;
}
return (
<Wrapper>
{/* ... */}
<div>
<DistanceMatrixService
options={{
origins: [autocompletedplace["formatted_address"]],
destinations: destinations,
travelMode: "DRIVING",
}}
callback={(response) => {
this.setState({ ...this.state, redirectToHome: true });
}}
/>
</div>
</Wrapper>
);
}
}
Related
I am doing a simple application which has a protected route.
The code goes like:
function App() {
return (
<>
<Router>
<Header />
<Routes>
<Route path="/" element={<Home />} />
<Route
path="/protected"
element={
<PrivateRoute>
<ProtectedExample />
</PrivateRoute>
}
/>
<Route path="/sign-in" element={<Login />} />
</Routes>
</Router>
</>
);
}
Private Route is :
import { Navigate } from "react-router-dom";
import { useAuthStatus } from "../hooks/useAuthStatus";
import Spinner from "./Spinner";
const PrivateRoute = ({ children }) => {
const { loggedIn, checkingStatus } = useAuthStatus();
if (checkingStatus) {
return <Spinner />;
}
return loggedIn ? children : <Navigate to="/sign-in" />;
};
export default PrivateRoute;
Protected Route
import React from "react";
const ProtectedExample = () => {
return <div>ProtectedExample</div>;
};
export default ProtectedExample;
Header.js
import React from "react";
import { Container, Nav, Navbar } from "react-bootstrap";
import { Link } from "react-router-dom";
const Header = () => {
return (
<Navbar bg="light" variant="light" fixed="top">
<Container>
<Navbar.Brand>
<Link to="/">Vivans</Link>
</Navbar.Brand>
<Nav className="me-auto">
<Link to="/sign-in" className="me-4">
Login
</Link>
<Link to="/protected">ProtectedRoute</Link>
</Nav>
</Container>
</Navbar>
);
};
export default Header;
When i remove the header component in App.js
The output is :
But when i add header component in App.js
The output is :
It doesn't return ProtectedExample as expected to be .
Why?
Actually, it might be rendering it. It maybe invisible for us because of CSS styling.
<Navbar bg="light" variant="light" fixed="top">
In the above line, you have given the NavBar to be fixed at the top. So the ProtectedExample would be behind the NavBar
Refer: https://developer.mozilla.org/en-US/docs/Web/CSS/position
Try changing your component like this,
const ProtectedExample = () => {
return <div style="margin-top: 3em">ProtectedExample</div>;
};
This might bring the content downwards to make it visible.
I'm trying to implement oauh login with react and spring boot and I've found a tutorial I can follow.
The issue I have is that it is using React Router v4, I would like to update it to use React Router v6 and using Functional components instead.
Login.js
import React, { Component } from 'react';
import './Login.css';
import { GOOGLE_AUTH_URL, FACEBOOK_AUTH_URL, GITHUB_AUTH_URL, ACCESS_TOKEN } from '../../constants';
import { login } from '../../util/APIUtils';
import { Link, Redirect } from 'react-router-dom'
import fbLogo from '../../img/fb-logo.png';
import googleLogo from '../../img/google-logo.png';
import githubLogo from '../../img/github-logo.png';
import Alert from 'react-s-alert';
class Login extends Component {
componentDidMount() {
// If the OAuth2 login encounters an error, the user is redirected to the /login page with an error.
// Here we display the error and then remove the error query parameter from the location.
if(this.props.location.state && this.props.location.state.error) {
setTimeout(() => {
Alert.error(this.props.location.state.error, {
timeout: 5000
});
this.props.history.replace({
pathname: this.props.location.pathname,
state: {}
});
}, 100);
}
}
render() {
if(this.props.authenticated) {
return <Redirect
to={{
pathname: "/",
state: { from: this.props.location }
}}/>;
}
return (
<div className="login-container">
<div className="login-content">
<h1 className="login-title">Login to SpringSocial</h1>
<SocialLogin />
<div className="or-separator">
<span className="or-text">OR</span>
</div>
<LoginForm {...this.props} />
<span className="signup-link">New user? <Link to="/signup">Sign up!</Link></span>
</div>
</div>
);
}
}
class SocialLogin extends Component {
render() {
return (
<div className="social-login">
<a className="btn btn-block social-btn google" href={GOOGLE_AUTH_URL}>
<img src={googleLogo} alt="Google" /> Log in with Google</a>
<a className="btn btn-block social-btn facebook" href={FACEBOOK_AUTH_URL}>
<img src={fbLogo} alt="Facebook" /> Log in with Facebook</a>
<a className="btn btn-block social-btn github" href={GITHUB_AUTH_URL}>
<img src={githubLogo} alt="Github" /> Log in with Github</a>
</div>
);
}
}
App.js
This is the App.js with the routes, I have updated it to use Functional components and React Router v6.
//imports left out
function App() {
const [globalUserState, setGlobalUserState] = useState({
authenticated: false,
currentUser: null,
loading: true
});
useEffect(() => {
loadCurrentlyLoggedInUser();
})
const loadCurrentlyLoggedInUser = () => {
getCurrentUser()
.then(res => {
setGlobalUserState({
currentUser: res,
authenticated: true,
loading: false
});
}).catch(err => {
setGlobalUserState({
loading: false
})
})
}
const handleLogout = () => {
localStorage.removeItem(ACCESS_TOKEN);
setGlobalUserState({
authenticated: false,
currentUser: null
});
Alert.success("You're safely logged out!");
}
return (
<Router>
<div className="app">
<div className="app-header">
<AppHeader />
</div>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/profile" element={<SecuredRoute> <Profile /> </SecuredRoute>} />
<Route path="/login" element={(props) => <Login authenticated={globalUserState.authenticated} {...props} />} />
<Route path="/signup" element={(props) => <Signup authenticated={globalUserState.authenticated} {...props} />} />
<Route path="/oauth2/redirect" element={<OAuth2RedirectHandler />} />
<Route path="*" element={<Notfound />} />
</Routes>
<Alert stack={{limit: 3}}
timeout = {3000}
position='top-right' effect='slide' offset={65}
/>
</div>
</Router>
);
}
export default App;
What I would like clarity on
I'm struggling to understand the equivalent of the react router functionalities with v6 (location.state.error, history.replace, location.pathname etc) and functional components instead of class based.
Also, If someone can explain this line please
<LoginForm {...this.props} />
Q1
I'm struggling to understand the equivalent of the react router
functionalities with v6 (location.state.error, history.replace,
location.pathname etc) and functional components instead of class
based.
In react-router-dom v6 there are no longer route props, i.e. no history, location, and no match. The Route components also no longer have component or render props that take a reference to a React component or a function that returns JSX, instead they were replaced by the element prop that takes a JSX literal, i.e. ReactElement.
If I'm understanding your question(s) correctly you are asking how to use RRDv6 with the class components Login and Signup.
You've a couple options:
Convert Login and Signup into React function components as well and use the new React hooks.
I won't cover the conversion, but the hooks to use are:
useNavigate - history object was replaced by a navigate function.
const navigate = useNavigate();
...
navigate("....", { state: {}, replace: true });
useLocation
const { pathname, state } = useLocation();
Create a custom withRouter component that can use the hooks and pass them down as props.
const withRouter = WrappedComponent => props => {
const navigate = useNavigate();
const location = useLocation();
// etc... other react-router-dom v6 hooks
return (
<WrappedComponent
{...props}
navigate={navigate}
location={location}
// etc...
/>
);
};
Decorate the Login and Signup exports:
export default withRouter(Login);
Swap from this.props.history.push to this.props.navigate:
componentDidMount() {
// If the OAuth2 login encounters an error, the user is redirected to the /login page with an error.
// Here we display the error and then remove the error query parameter from the location.
if (this.props.location.state && this.props.location.state.error) {
setTimeout(() => {
const { pathname, state } = this.props.location;
Alert.error(state.error, { timeout: 5000 });
this.props.navigate(
pathname,
{ state: {}, replace: true }
);
}, 100);
}
}
What remains is to fix the routes in App so they are correctly rendering JSX.
<Router>
<div className="app">
<div className="app-header">
<AppHeader />
</div>
<Routes>
<Route path="/" element={<Home />} />
<Route
path="/profile"
element={(
<SecuredRoute>
<Profile />
</SecuredRoute>
)}
/>
<Route
path="/login"
element={<Login authenticated={globalUserState.authenticated} />}
/>
<Route
path="/signup"
element={<Signup authenticated={globalUserState.authenticated} />}
/>
<Route path="/oauth2/redirect" element={<OAuth2RedirectHandler />} />
<Route path="*" element={<Notfound />} />
</Routes>
<Alert stack={{limit: 3}}
timeout = {3000}
position='top-right' effect='slide' offset={65}
/>
</div>
</Router>
Q2
Also, If someone can explain this line please <LoginForm {...this.props} />
This is simply taking all the props that were passed to the parent component and copying/passing along to the LoginForm component.
<LoginForm {...this.props} />
Login is passed an authenticated prop as well as whatever new "route props" were injected, and any other props injected by any other HOCs you may be using, and the above passes them all along to LoginForm.
I have a web app which is under development which is just like google drive using firebase. I have this useParams() in Dashboard Screen which is the main page of the App with All the different Folder Routes. So for this screen i have used useParams and now when i console.log(params) it shows an empty object {} and also when i click the button it does not navigate only the URL changes
Github Code :- https://github.com/KUSHAD/RDX-Drive/
In App.js
import { BrowserRouter, Switch, Route } from 'react-router-dom';
import PrivateRoute from './Components/Route/PrivateRoute';
import Dashboard from './Screens/Main/Dashboard';
import ViewProfile from './Screens/Profile/ViewProfile';
import Signup from './Screens/Auth/Signup';
import Login from './Screens/Auth/Login';
import ForgotPassword from './Screens/Auth/ForgotPassword';
function App() {
return (
<>
<div className='App'>
<div className='main'>
<BrowserRouter>
<Switch>
{/* Drive */}
<PrivateRoute exact path='/' component={Dashboard} />
<PrivateRoute
exact
path='/folder/:folderId'
component={Dashboard}
/>
{/* Profile */}
<PrivateRoute path='/profile' component={ViewProfile} />
{/* Auth */}
<Route path='/signup' component={Signup} />
<Route path='/login' component={Login} />
<Route path='/forgot-password' component={ForgotPassword} />
</Switch>
</BrowserRouter>
</div>
</div>
</>
);
}
export default App;
In Dashboard.js
import NavBar from '../../Components/Shared/NavBar';
import Container from 'react-bootstrap/Container';
import AddFolderButton from '../../Components/Main/AddFolderButton';
import { useDrive } from '../../services/hooks/useDrive';
import Folder from '../../Components/Main/Folder';
import { useParams } from 'react-router-dom';
export default function Dashboard() {
const params = useParams();
console.log(params);
const { folder, childFolders } = useDrive();
return (
<div>
<NavBar />
<Container fluid>
<AddFolderButton currentFolder={folder} />
{childFolders.length > 0 && (
<div className='d-flex flex-wrap'>
{childFolders.map(childFolder => (
<div
key={childFolder.id}
className='p-2'
style={{ maxWidth: '250px' }}>
<Folder folder={childFolder} />
</div>
))}
</div>
)}
</Container>
</div>
);
}
Issue
After scouring your repo looking for the usual suspect causes for "it does not navigate only the URL changes" I didn't find anything odd like multiple Router components, etc. I think the issue is your PrivateRoute component isn't passing the props to the Route correctly. You're destructuring a prop called rest and then spread that into the Route, but you don't pass a rest prop to the PrivateRoute
export default function PrivateRoute({ component: Component, rest }) { // <-- rest prop
const { currentUser } = useAuth();
return (
<Route
{...rest} // <-- nothing is spread/passed here
render={props => {
return currentUser ? (
<Component {...props} />
) : (
<Redirect to='/login' />
);
}}
/>
);
}
The routes, these are not passed any prop named rest:
<PrivateRoute exact path='/' component={Dashboard} />
<PrivateRoute
exact
path='/folder/:folderId'
component={Dashboard}
/>
What I believe to be occurring here is the exact and path props aren't passed to the underlying Route component and so the first nested component of the Switch is matched and rendered, the "/" one that doesn't have any route params.
Solution
The fix is to spread the rest of the passed props into rest instead of destructuring a named rest prop.
export default function PrivateRoute({ component: Component, ...rest }) {
const { currentUser } = useAuth();
return (
<Route
{...rest}
render={props => {
return currentUser ? (
<Component {...props} />
) : (
<Redirect to='/login' />
);
}}
/>
);
}
An improvement of your private route may be as follows:
export default function PrivateRoute(props) {
const { currentUser } = useAuth();
return currentUser ? (
<Route {...props} />
) : (
<Redirect to='/login' />
);
}
This checks your user authentication and renders either a Route or Redirect. This pattern allows you to use all the regular Route props so you aren't locked into using the render prop to render the component.
Hello I'm having a problem with setting 'react-router-modal' parentPath for the last used component
ModalRoute and ModalContainer are a part of
react-router-modal
App.js
class App extends Component {
render() {
return (
<main>
<Navbar />
<BrowserRouter>
<div>
<Switch>
<Route path="/main" component={ContentDefault} />
<Route path="/search" component={SearchScreen} />
<ModalRoute
path="/register"
parentPath="/"
component={RegisterScreen}
/>
<Route path="/contact" component={ContentDefault} />
<Route component={ContentDefault} />
</Switch>
<ModalContainer />
</div>
</BrowserRouter>
<Footer />
</main>
);
}
}
export default App;
SearchScreen.jsx
import React, { Component } from "react";
import { withRouter } from "react-router-dom";
class SearchScreen extends Component {
render() {
return (
<main>
<h1>SearchScreen</h1>
</main>
);
}
}
export default withRouter(SearchScreen);
For Example i'm on mainScreen then i move to the SearchScreen then i open modal from navbar. I need my modal to go back to the SearchScreen
I found couple of solutions which may help you.
You can try with this:
Create state prevPath
Add componentWillReceiveProps
Provide prevPath state to the parentPath with condition if prevPath is empty redirect me on route '/'
class App extends Component {
state = {
prevPath: ''
}
componentWillReceiveProps(nextProps) {
if (nextProps.location !== this.props.location) {
this.setState({ prevPath: this.props.location })
}
}
<ModalRoute
path="/register"
parentPath={this.state.prevPath || '/'}
component={RegisterScreen}
/>
This is one of the solutions we can try later another one if this doesn't help.
I have a component (DatasetPage) which renders some images of different datasets for an image selected. This depends on the tab clicked on the navigation top bar. The thing is that one of the dataset in my case is very big and so it takes more time to load the page. If I wait until the page is loaded everything works well but, if I click into another tab (another dataset) before the reducer delivers the properties to my component (ImageWithRelateds), the new page is loaded with the information of the other(last) dataset, which was not loaded yet.
So, I have thought about a solution which could be block the navigation through the navigation bar while I have the Loading running. But the thing is that this loading thing is controlled in the ImageWithRelateds.js component and the navigation bar is controlled from App.js. So I would need to access from App.js to the isLoading attribute of ImageWithRelateds.js (which I already have) but I don't know how to do it. I just found ways to access from children to parent attributes but not backwards. If you could help me with that or just proposing another solution I would be very grateful.
App.js
import React, { Component } from 'react';
import { IndexLink } from 'react-router';
import '../styles/app.scss';
class App extends Component {
constructor(props){
super(props);
this.options = this.props.route.data;
}
renderContent(){
if(this.props.route.options) {
return(<div className="navbar-header nav">
<a className="navbar-brand" id="title" href="/" >
IMAGES TEST WEB
</a>
<li className="nav-item" key={`key-9999`}>
<IndexLink to='/home' className="nav-link active" href="#">HOME</IndexLink>
</li>
{this.props.route.options.map((opt,i)=>{
return this.returnOptions(opt,i);
})}
</div>
);
}
}
returnOptions(opt,i){
return(<li className="nav-item" key={`key-${i}`}>
<IndexLink to={opt.link} className="nav-link active"
href="#">{opt.name}</IndexLink>
</li>);
}
render() {
return (
<div className="main-app-page">
<nav className="navbar navbar-default color-navbar fixed">
<div className="container-fluid">
{this.renderContent()}
</div>
</nav>
<div className="content">
{this.props.children}
</div>
</div>
);
}
}
export default App;
Routes.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Route, IndexRoute, browserHistory, Link } from 'react-router';
import App from './common/App';
import NotFound from './common/NotFound';
import Store from './store';
import Home from './components/Home';
import DatasetPage from './components/Images/DatasetPage';
import ImageWithRelateds from './components/Images/ImageWithRelateds';
import { options_NavBar } from './customize.js';
import { getQimList, resetQimList} from './actions/index';
const Test = ()=>{
return(<h2 style={{"paddingLeft":"35%"}} >W E L C O M E !</h2>)
};
export default (
<Route path="/" components={App} options={options_NavBar} history={browserHistory}>
<IndexRoute components={Test}/>
<Route path="/home" component={Home} />
<Route path="images" >
<Route path="oxford" component={DatasetPage} onEnter={()=>{
Store.dispatch(resetQimList());
Store.dispatch(getQimList('oxford'));
}} />
<Route path="paris" component={DatasetPage} onEnter={()=>{
Store.dispatch(resetQimList());
Store.dispatch(getQimList('paris'));
}} />
<Route path="instre" component={DatasetPage} onEnter={(e)=>{
Store.dispatch(resetQimList());
Store.dispatch(getQimList('instre'));
}} />
<Route path=":id" component={ImageWithRelateds} />
</Route>
<Route path="*" component={NotFound} />
</Route>
);
Thank you so much!
One of the most basic principle in react that the parent give props to children, and the children emit events to the father (to avoid 2 way binding)
so, your App.js should have state, with isLoading variable, and to the ImageWithRelateds component you should pass an event (function) something like this:
<Route path=":id" render={(props) => <ImageWithRelateds {...props} onFinishLoading={loadingFinished} />}>
and inside your component (that should be with state) should have function like this:
function loadingFinished() {
this.setState(prev => ({ ...prev, isLoading: false }))
}
and then, you would know inside you App.js if the loading inside the ImageWithRelateds component finished, and then you would able to do any validation you would like
I suggest to you to read this article about passing events (functions) to components, why it's needed and how to do it effectively
Hope that helped!
Edit:
your final Routes.js code should look something like that:
export default class Routes extends React.Component {
constructor() {
super();
this.state = { isLoading: false };
this.onLoadingFinishded = this.onLoadingFinishded.bind(this);
}
onLoadingFinishded() {
this.setState(state => {
...state,
isLoading: false
});
}
render() {
return <Route path="/" components={App} options={options_NavBar} history={browserHistory}>
<IndexRoute components={Test}/>
<Route path="/home" component={Home} />
<Route path="images" >
<Route path="oxford" component={DatasetPage} onEnter={()=>{
Store.dispatch(resetQimList());
Store.dispatch(getQimList('oxford'));
}} />
<Route path="paris" component={DatasetPage} onEnter={()=>{
Store.dispatch(resetQimList());
Store.dispatch(getQimList('paris'));
}} />
<Route path="instre" component={DatasetPage} onEnter={(e)=>{
Store.dispatch(resetQimList());
Store.dispatch(getQimList('instre'));
}} />
<Route path=":id" render={(props) => <ImageWithRelateds
{...props}
onLoadingFinished={this.onLoadingFinishded} />} />
</Route>
<Route path="*" component={NotFound} />
</Route>
}
}
(i can't ensure that code exactly running because i don't have all of your project, but that most likely it)