What is the best solution to pass data between componenets in React - reactjs

This is my App.js:
function App() {
return (
<BrowserRouter>
<div>
<Routes>
<Route path="/" exact element={<Home />} />
<Route path="/creation" element={<Creation />} />
<Route path="/payment" element={<Payment />} />
</Routes>
</div>
</BrowserRouter>
);
}
export default App;
I need to pass a string generated in the "Creation page" to the "Payment page."
Meanwhile, once the string is generated on the creation page, the user should be automatically directed to the payment page. ( I have a solution for this: useNavigate( ) ).
Does anyone know what is the best solution to reach this goal?
I appreciate any help you can provide.

In React Router v6 you can pass parameters to the url your navigating to
navigate(`/payment/${yourString}`);
then in payments:
import { useParams, useNavigate } from "react-router-dom";
const params = useParams();
then you can access your string with params.yourString.

Related

How can I create an effective private route using the latest version of react-router-dom

I've looked online and most questions uses a different version of react-router-dom than what I'm using making the answer hard to find. What I want to do is simple, let's say a user is logged in then I wouldn't want that user to access the "sign-up" page and redirect them to the "home" page.
Here's the code I'm using that isn't working.
import { useAuth } from '../contexts/AuthContext';
import "firebase/auth";
import {Route, Navigate} from 'react-router-dom'
function AuthRoute ({element: Element, ...rest}) {
const { currentUser } = useAuth()
return (
<Route
{...rest}
render={props => {
return !currentUser ? <Element {...props} /> : <Navigate to="/" />
}}
></Route>
)
}
export default AuthRoute;
Here's how it's being called in App.js
return (
<Router>
<div className = "App">
<AuthProvider>
<Routes>
<Route path="/" element={<Home/>}/>
<AuthRoute exact path = "/sign_up" element= {SignUp} />
<Route exact path="/about" element={<About/>}/>
<Route exact path="/login" element={<SignIn/>}/>
</Routes>
</AuthProvider>
</div>
</Router>
);
It routes to sign_up but it doesn't matter if the user exists or not.
I don't know if you still need help with this but I found how to do it,
first your home route should be an exact path, then in AuthRoute() component you can do this:
export default function AuthRoute ({chidren}) {
const { currentUser } = useAuth()
if (currentUser){
return (children)
}else{
return <Navigate to='/sign_up' replace/>
}
}
then in App.js:
<AuthRoute exact path = "/sign_up" element= {SignUp} /> // should be :
<Route path='/sign_up' element={<AuthRoute> <Home/> </AuthRoute>}
</Route>
hope this can help anyone struggling with react-router v6 (like I did)

Protected routes for react router v5 isn't working

I'm trying to create a protected/private route with react-router-dom v5, as in the link here, I use only the code I needed - Protected routes and authentication with React Router v5.
Now I need both components private from each other
The problem: after Getting Home component transferred fromSignUp components I can go back to SignUp and thats not what I want.
Signup corrently is the first page (as sign-in). I don't want the user to go back to sigh-up components if he already signed up and present at the Home component.
The working website project is here - CodeSandbox
Anyone has idea how can I make both component protected/private?
GuardedRoute.jsx
function GuardedRoute({ children, auth, ...rest }) {
return (
<Route
{...rest}
render={() => {
return auth === true ? children : <Redirect to="/signup" />;
}}
/>
);
}
export default GuardedRoute;
App.js
const App = () => {
const [userID, setUserID] = useState('');
const [userSignedUp, setIfSignUp] = useState(false);
return (
<div className="App-div">
<GuardedRoute exact path="/home" auth={userSignedUp}>
<Home userIDNumber={userID} setIfSignUp={setIfSignUp} />
</GuardedRoute>
<Switch>
<Route path="/signup">
<SignUp setUserNumber={setUserID} setIfSignUp={setIfSignUp} />
</Route>
</Switch>
</div>
);
};
export default App;
Please try any of your solutions at my codesandbox before posting your answer so we will not try solutions in theory only and in vain :)
You could make the signup route only exist if the user is not logged in, and then use a catch-all that will redirect to /home
<div className="App-div">
<Switch>
{!userSignedUp && (
<Route path="/signup">
<SignUp setUserNumber={setUserID} setIfSignUp={setIfSignUp} />
</Route>
)}
<GuardedRoute path="/home" auth={userSignedUp}>
<Home userIDNumber={userID} setIfSignUp={setIfSignUp} />
</GuardedRoute>
<Route path="/">
<Redirect to="/home" />
</Route>
</Switch>
</div>
Updated sample: https://codesandbox.io/s/jolly-https-t7dmj?file=/src/containers/App.js
or you could encapsulate the logic to another component like GuardedRoute, let say UnguardedRoute that will redirect somewhere if user is logged in.

Adding MsalAuthenticationTemplate for some routes causes all routes to require login

I’ve got a ReactJS website in which I am trying to use "#azure/msal-react": "^1.0.0-beta.1", and ran into some issues that have me flummoxed.
Most of my pages are open to the public. Some require login. If I add the MSALAuthenticationTemplate as below (but with interactionType=Redirect), as soon as I start the site, it asks me to login. I thought it would only do that if I hit a route that was in the AuthenticationTemplate.
Using InteractionType Popup causes the SPA to throw an exception on startup
Error: Objects are not valid as a React child (found: object with keys {login, result, error}). If you meant to render a collection of children, use an array instead. in p (at App.tsx:44)
All of my routes are, for some reason, coming back to the home page instead of loading the relevant components, event with the AuthenticationTemplate commented out.
I had this pretty much working using straight Javascript, but was running into ESLint issues when publishing, so I thought Typescript would help me fix those. But now it’s just broke.
render() {
initializeIcons();
return (
<MsalProvider instance={msalClient} >
<div className="d-flex flex-column h-100">
<TopMenu />
<div className="container-fluid flex-grow-1 d-flex">
<div className="row flex-fill flex-column flex-sm-row">
<BrowserRouter>
<MsalAuthenticationTemplate
interactionType={InteractionType.Popup}
errorComponent={this.ErrorComponent}
loadingComponent={this.LoadingComponent}>
<Switch>
<Route path="/addevent">
<AddEvent />
</Route>
<Route path="/mydashboard">
<MyDashboard />
</Route>
</Switch>
</MsalAuthenticationTemplate >
<UnauthenticatedTemplate>
<Switch>
<Route path='/'>
<Home />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="/contactus">
<ContactUs />
</Route>
<Route path="/faq">
<Faq />
</Route>
<Route path="/fetchevents">
<FetchEvents />
</Route>
<Route path="/gettingstarted">
<GettingStarted />
</Route>
<Route path="/partners">
<Partners />
</Route>
<Route path="/privacypolicy">
<PrivacyPolicy />
</Route>
<Route path="/sponsors">
<Sponsors />
</Route>
<Route path="/termsofservice">
<TermsOfService />
</Route>
<Route path="/userstories">
<UserStories />
</Route>
</Switch>
</UnauthenticatedTemplate>
<div>
<Footer />
</div>
</BrowserRouter>
</div>
</div>
</div>
</MsalProvider>
);
Let's start with the UnauthenticatedTemplate. If the user is authenticated, children of the component will not show. So I guess you don't want to use it there. It's typical usage is for Login/Logout button for example.
Another problem is that if you are using MsalAuthenticationTemplate as the parent of the Switch and Route components. The problem is that you are guarding switch and routes from unauthenticated users, but this components should always be available without authentication, if you don't want to protect whole page.
During rendering React will go through your components one by one and first child of the BrowserRouter component it will try to render is MsalAuthenticationTemplate and since user is not authenticated, it will redirect user to login page.
This is quote from react-router docs:
A Route is always technically “rendered” even though it’s rendering null. When the 's path matches the current URL, it renders its children (your component).
Because of this the children of the route will only be rendered if the route will be hit. So you need to put MsalAuthenticationTemplate component as a direct child of the route, or even inside such component:
<Switch>
<Route path="/addevent">
<MsalAuthenticationTemplate
interactionType={InteractionType.Redirect}
authenticationRequest={loginRequest}
>
<AddEvent />
</MsalAuthenticationTemplate>
</Route>
...
</Switch>
As for all the webpages redirected to your home screen, you should add exact keyword to your Home route. This attribute causes it to not match all other routes also. Single '/' matches all your other routes.
In addition to the answer already provided, there is a way (cleaner in my opinion) you can configure MSAL react to take advantage of the router's navigate functions when MSAL redirects between pages in your app.
Here is how it works:
In your index.js file you can have something like so:
import { PublicClientApplication, EventType } from "#azure/msal-browser";
import { msalConfig } from "./authConfig";
export const msalInstance = new PublicClientApplication(msalConfig);
ReactDOM.render(
<React.StrictMode>
<Router>
<ThemeProvider theme={theme}>
<App pca={msalInstance} />
</ThemeProvider>
</Router>
</React.StrictMode>,
document.getElementById('root')
);
As shown, you need to pass msal instance as props to your main App.
Then in your App.js where you setup your routes, you will need to do the following:
import { MsalProvider } from "#azure/msal-react";
import { CustomNavigationClient } from "NavigationClient";
import { useHistory } from "react-router-dom";
function App({ pca }) {
// The 3 lines of code below allow you to configure MSAL to take advantage of the router's navigate functions when MSAL redirects between pages in your app
const history = useHistory();
const navigationClient = new CustomNavigationClient(history);
pca.setNavigationClient(navigationClient);
return (
<MsalProvider instance={pca}>
<Grid container justify="center">
<Pages />
</Grid>
</MsalProvider>
);
}
function Pages() {
return (
<Switch>
<Route path="/addevent">
<AddEvent />
</Route>
<Route path="/mydashboard">
<MyDashboard />
</Route>
<Route path='/'>
<Home />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="/contactus">
<ContactUs />
</Route>
<Route path="/faq">
<Faq />
</Route>
// your other routes
</Switch>
)
}
And here is the helper function used in App.js that enables navigation by overriding the the default function used by MSAL
import { NavigationClient } from "#azure/msal-browser";
/**
* This is an example for overriding the default function MSAL uses to navigate to other urls in your webpage
*/
export class CustomNavigationClient extends NavigationClient{
constructor(history) {
super();
this.history = history;
}
/**
* Navigates to other pages within the same web application
* You can use the useHistory hook provided by react-router-dom to take advantage of client-side routing
* #param url
* #param options
*/
async navigateInternal(url, options) {
const relativePath = url.replace(window.location.origin, '');
if (options.noHistory) {
this.history.replace(relativePath);
} else {
this.history.push(relativePath);
}
return false;
}
}
You can then use AuthenticatedTemplate on your private pages and UnauthenticatedTemplate on the public pages. For example if you have have addEvent.js (private) and Home.js (public), you will have each components like so:
export function Home() {
return (
<>
<AuthenticatedTemplate>
<p>Welcome Home - it's public</p>
</AuthenticatedTemplate>
</>
);
}
export function AddEvent() {
return (
<>
<UnauthenticatedTemplate>
<Typography variant="h6">
Add event - it is a private page
</Typography>
</UnauthenticatedTemplate>
</>
);
}
Here is a complete example on how to use react-router with msal react for your reference.

Reactjs - How to re-direct to default home page

I am trying to set redirection to default page, but not works. what is the correct way to do this?
const routes = [
{
path:"/",
component:Home,
extract:"true",
redirect:"/home"
},
{
path:"/home",
component:Home
},
{
path:"/service",
component:Service
}
];
html:
<div>
<Router>
<Header />
{routes.map((route) => (
<Route
key={route.path}
path={route.path}
component={route.component}
/>
))}
</Router>
Assuming you are using react-router-dom, I can see one definitive issue with some additional concerns. You may only need to make one change, but I would advise reviewing the rest of your code to ensure the best routing config.
Main Issue: You have to add a redirect to '/home' if you want that component to be the first page to be loaded. This is because when the app is rendered, it sees the default path as '/'.
<Redirect exact from="/" to="/home" />
<Route path="/home">
<Home />
</Route>
While this may solve your problem by itself, here is a more comprehensive solution that should be beneficial to compare your current code against.
import {
BrowserRouter as Router,
Switch,
Route,
Redirect
} from "react-router-dom";
import home from "./Home";
import service from "./Service";
import header from "./Header";
function App () {
return (
<div>
<Router>
<Header />
<hr />
<Switch>
<Redirect exact from="/" to="/home" />
<Route path="/home">
<Home />
</Route>
<Route exact path="/service">
<Service />
</Route>
</Switch>
</Router>
</div>
);
}
export default App;
As you can see, I would recommend moving the Header outside of the Switch statement and then applying the redirect from / to /home.
Also, please note this configuration is simply an example. It depicts a situation where you are exporting App as a component and does not account for login authorization, so certain aspects of your code may vary.

react-router-dom match object isExact false

I am working on a react project. I try to access the url parameters in the Header component. However, it always returns empty.
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import { ConnectedRouter } from 'connected-react-router'
import SamplePage from './pages/SamplePage';
import PropertyPage from './pages/PropertyPage';
import LoadingPage from './pages/LoadingPage';
import Header from './header/Header';
import ButtonGroup from './ButtonGroup';
import { Container } from 'semantic-ui-react';
import history from '../history';
const App = () => {
return (
<ConnectedRouter history={history}>
<div>
<Switch>
<Route path='/loading' exact component={LoadingPage} />
<Route component={Header} title='Sample page' />
</Switch>
<Container style={{ marginTop: '7em' }}>
<Switch>
<Route
path='/page/:pageType/properties/:propertyId'
exact
component={PropertyPage}
/>
<Route path='/page/:pageType' exact component={SamplePage} />
</Switch>
</Container>
<Switch>
<Route exact path='/loading' render={() => <div />} />
<Route component={ButtonGroup} />
</Switch>
</div>
</ConnectedRouter>
);
}
export default App;
I try to access url params in the Header component. The params is empty, and isExact is false. Can anyone help me with this? Thanks.
From screenshot of console.log, react-router is matching on
<Route component={Header} title='Sample Scorecard' />
This is correct behavior as Switch looks for the first match.
I suggest to not declare rendering for Header as a Route. i.e.
<Switch>
<Route path='/loading' exact component={LoadingPage} />
<Header title='Sample Scorecard' />
</Switch>
This way Switch will only render it when loading path isn't matched.
I still cannot figure out how to solve this issue. What I do to walk around this issue is to create a Higher Order Component. Header will be included in the HOC, then it has no problem to get the URL parameters.

Resources