Where can I put auto-authentication code in ReactJS? - javascript

I am using ReactJS, redux, and react-router. I am using localStorage to log in users automatically. My App.js looks like this:
` <Router>
<Provider store={store}>
<div className="App">
<Route path="/" component={NavBar} />
<Route exact path="/" component={Home} />
<Route exact path="/list" component={RoadmapList} />
<Route path="/roadmap" component={OneRoadmap} />
<Route path="/search" component={Search} />
<Route exact path="/add" component={RoadmapAdder} />
<Route exact path="/register" component={Register} />
<Route exact path="/login" component={Login} />
<Route
exact
path="/savedRoadmaps"
component={SavedRoadmapsView}
/>
<Route path="/profile" component={AuthorProfile} />
<Route exact path="/account/edit" component={Profile} />
</div>
</Provider>
</Router>`
I previously did auto-authentication in my NavBar component since NavBar will be in every single URL because of the <Route path="/" component={NavBar}/>, I thought I could've just used NavBar to auto-login my users using a redux action called loginUser and then update a few redux states like isAuthenticated and user_info. The problem is that if I were to render NavBar and let's say SavedRoadmapsView, SavedRoadmapsView wouldn't get the redux states immediately since it runs parallel with NavBar.
I have tried to login Users automatically in App.js, like:
` async componentDidMount() {
var email = localStorage.getItem("email");
var password = localStorage.getItem("password");
var userData = { email: email, password: password };
await loginUser(userData);
}`
It didn't seemed to work. Could it be that loginUser is a redux action and can't be used outside of Provider? What are your insights on this? Thanks in advance.

Related

useParams() is empty in react-router

I'm trying to render the params in the url as an h2 in in the website. But even when I try console.log useParams is empty.
Here's my Router.js file
const Webpages = () => {
return (
<Router>
<Routes>
<Route exact path="/" element={Home()} />
<Route exact path="/comics" element={Comics()} />
<Route path='/comics/:comicId' element={Comic()} /> <--------------
<Route exact path="/portfolio" element={Portfolio()} />
<Route exact path="/blog" element={Blog()} />
<Route exact path="/contact" element={Contact()} />
<Route exact path="/store" element={Store()} />
<Route path="*" element={NotFound()} />
</Routes>
</Router>
);
};
export default Webpages;
Here's my comic component
import React from 'react';
import NavBar from '../../components/NavBar';
import { useParams } from 'react-router-dom';
function Comic() {
let { comicId } = useParams();
console.log(comicId);
return (
<div>
<NavBar />
<p>{comicId}</p>
</div>
)
}
export default Comic
The page element when I go to a random comic works fine, like localhost:3000/comics/465456 but the tag is empty and the console log is undefined, it's also undefined if I just try to console log useParams()
You are directly invoking the React function. Directly invoking React functions is not how React works.
The Route component's element prop expects a React.ReactNode, a.k.a. JSX. Pass the components as JSX. The JSX is transpiled down to vanilla Javascript and the React framework handles calling your function within the confines of the React component lifecycle.
Example:
const Webpages = () => {
return (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/comics" element={<Comics />} />
<Route path='/comics/:comicId' element={<Comic />} />
<Route path="/portfolio" element={<Portfolio />} />
<Route path="/blog" element={<Blog />} />
<Route path="/contact" element={<Contact />} />
<Route path="/store" element={<Store />} />
<Route path="*" element={<NotFound />} />
</Routes>
</Router>
);
};

React toggle global Component

I got all my Routes in my App.js like this:
<Route exact path="/" component={Home} />
<Route path="/explore" component={Explore} />
<Route path="/register" component={Register} />
<ProtectedRoute path="/create" component={CreatePost} />
and I want to toggle an Success/Error Alert Component as Feedback but I want to display it above the whole app no matter which component is rendered right now. So I am stuck and don't know what is the best way to do so. I tried to write a function to toggle the Alert in App.js and export it but it didn't work for me to import it and change the state.
onShowAlert = () => {
this.setState({ showAlert: true };
};
You can use render assignment and pass your feedback trigger to the component. Is this what you are asking?
function myTigger(){ console.log('triggered') }
<Route exact path="/" render={ () => <Home trigger={myTigger} />} />
<Route path="/explore" component={Explore} />
<Route path="/register" component={Register} />
<ProtectedRoute path="/create" component={CreatePost} /
If you're trying to avoid passing the toggler as a prop, then create an AlertContext:
https://codesandbox.io/s/great-moon-93c94?file=/src/AlertContext.js

Nested React Router : hide parent component on showing nested child component

Being a beginner in reactJS, I want to know how to hide parent component when i route to child component URL
Assume a scenario:
User is at "/house" as this:
<Route path="/house" component={House} />
when user clicks a house grid he navigates to "/house/flat/:flatID". Inside House component
<Route
path={`${this.props.match.url}/flat/:flatId`}
render={() => <div>Each Flat details</div>}
/>
then both child and parent components are visible like this:
So what i want is to show only flat component when user navigates to "/house/flat:FlatId". Please suggest me anything that helps ! Any links to article so that i can learn and achieve such functionality.
Code:
App.js
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/account" component={Account} />
<Route path="/gharjagga" component={GharJagga} />
</Switch>
House.js
onGharGridClick= id => {
<Redirect to={`${this.props.match.url}/flat/${id}`} />;
};
return (
<Route
path={`${this.props.match.url}/flat/:fkatId`}
render={() => <div>Ghar Each</div>}
/>
);
You can achieve it different ways
define routes in the parent component, I think this is the best option.
<Switch>
<Route path="/account" component={Account} />
<Route path="/house/flat/:flatId" component={FlatComponent}/>
<Route path="/house" component={House} />
<Route path="/" component={Home} />
</Switch>
Note: instead of using exact, order your routes based on priority, that will make the route to redirect to next matching route if any typo in the route entered
You can make House as separate route component, and nest the routes inside that component
// Routes
<Switch>
<Route path="/account" component={Account} />
<Route path="/house" component={House} />
<Route path="/" component={Home} />
</Switch>
// House component
class House extends React. Components {
render() {
return (
<Switch>
<Route path="/house/flat/:flatId" render={() => <div>Each Flat details</div>} />}/>
<Route path="/house" component={HouseGridComponent} />
</Switch>
)
}
}
you can check whether the route has flatId and hide the elements, in your House component you can check this.props.match.params.flatId, if the flatId is set you can hide that House Component.
// House Component
class House extends React. Components {
render() {
return (
<div>
{
!this.props.match.params.flatId?
<h1>House Component</h1>:
null
}
<Route path={`${this.props.match.url}/flat/:flatId`} render={() => <div>Each Flat details</div>} />
</div>
)
}
}
The solution is to raise "/house/flat/:flatId" to the same level as "/house".
<Switch>
<Route exact path="/" component={Home} />
<Route path="/account" component={Account} />
<Route path="/house/flat/:flatId" render={() => <div>Each Flat details</div>}/>
<Route path="/house" component={House} />
</Switch>

relative routing in react-router

Did anyone know how to create for any path that ends with '/popup-image-:id' ? i have website where each image could be opened in popup on any page. So, if i couldn't implement '/popup-image-:id' i have to double each route in my website to place inside. I would like avoid redundance and fragility.
my current routing:
<Router history={browserHistory}>
<Route path="/" component={Base}>
<Route path="profile" component={Profile}>
<Route path="popup-image-:id" component={Full_Piture} />
</Route>
<Route path="feed" component={Feed_List}>
<Route path="popup-image-:id" component={Full_Piture} />
</Route>
<Route path="user-:id" component={User_Page}>
<Route path="popup-image-:id" component={Full_Piture} />
</Route>
</Route>
</Router>
As u can see i had to produce a lot of duplicated code. If i could write something like <Route path="*/popup-image-:id" component={Full_Piture} /> it will be much cleaner and solid code
Is the parent <Route> also supposed to match? If it isn't, you should be able to use the ** syntax.
<Route path="/**/popup-image-:id" component={Full_Picture} />
If the parent <Route> should also match, you could create a <RouteWithPopup> component that returns a <Route> which has the <Route path='popup-image-:id'> as its child.
const RouteWithPopup = (props) => {
return (
<Route {...props}>
<Route path="popup-image-:id" component={Full_Picture} />
</Route>
)
}
Then you could turn your route config into:
<Router history={browserHistory}>
<Route path="/" component={Base}>
<RouteWithPopup path="profile" component={Profile} />
<RouteWithPopup path="feed" component={Feed_List} />
<RouteWithPopup path="user-:id" component={User_Page} />
</Route>
</Router>

Clean way to re-route user that isn't logged in with React?

I guess I could always add login in WillTransitionTo which would do a call to the store via Redux to verify that the user is logged in but this seems a bit hacky, especially since since I have to add WillTransitionTo() logic to every single component.
Anyone have any better ideas? I'm going to work toward wiring something into isAuthorized with the onEnter.
This is the root root of the application which performs the react-router wire-up.
react-router : 2.0.4
react: 15.0.2
const store = configureStore();
function isAuthorized() {
}
render(
<Provider store={store}>
<Router history={browserHistory}>
<Route path="/" component={Application} onEnter={isAuthorized}>
<IndexRoute component={DashboardPage} />
<Route path="login" component={LoginPage} />
<Route path="dashboard" component={DashboardPage} />
<Route path="items" component={DashboardPage} />
<Route path="item-categories" component={DashboardPage} />
<Route path="modifiers" component={DashboardPage} />
<Route path="taxes" component={DashboardPage} />
<Route path="discounts" component={DashboardPage} />
<Route path="orders" component={DashboardPage} />
<Route path="users" component={DashboardPage} />
</Route>
</Router>
</Provider>,
document.getElementById("application")
);
Note that the example in auth-flow utilizes localStorage to persist userAccess tokens, etc. I really don't want to use localStorage or cookies at this time. I want to minimize the complexity and store the login state in the store(state.user). I don't specifically want to go into the reason for this other than to say that there is a large amount of information that comes the authentication step that needs to be persisted to the store.
You can try doing something like
<Route
path="/"
component={Application}
onEnter={ (nextState, replace) => { isAuthorized(nextState, replace, store) } } >
And in your isAuthorized function
function isAuthorized( nextState, replace, store ) {
// Now you have access to the store, you can
const state = store.getState();
// your logic to validate whether the user is logged in or not
if ( state.user && state.user.loggedIn === true ) {
// this user is authenticated
// continue..
}
else {
// unauthenticated user, redirect the user to login page
replace({
pathname: '/login',
state: { nextPathname: nextState.location.pathname }
});
}
}
(Posted solution on behalf of the OP).
This solution worked for me which is a slight modification on the answer:
function isAuthorized(nextState, replace) {
if (nextState.location.pathname === "/login") {
return;
}
const state = store.getState();
if (state.user && state.user.loggedIn === true) {
return;
}
replace("/login");
}
render(
<Provider store={store}>
<Router history={browserHistory}>
<Route path="/" component={Application} onEnter={(nextState, replace) => isAuthorized(nextState, replace)}>
<IndexRoute component={DashboardPage} />
<Route path="login" component={LoginPage} />
<Route path="dashboard" component={DashboardPage} />
<Route path="items" component={DashboardPage} />
<Route path="item-categories" component={DashboardPage} />
<Route path="modifiers" component={DashboardPage} />
<Route path="taxes" component={DashboardPage} />
<Route path="discounts" component={DashboardPage} />
<Route path="orders" component={DashboardPage} />
<Route path="users" component={DashboardPage} />
</Route>
</Router>
</Provider >,
document.getElementById("application")
);

Categories

Resources