I have a react app and I serving it with Node js. I currently have a jwt token implemented in node js. So, when I login a token is sent back to the client and then stored in localstorage.
To verify that the user is logged in and has rights to access some of the pages. I will need to
Get the token from local storage
Check the expiry of the token. And if it is expired redirect them back to the login page.
I currently cannot get passed the 1st step
Here is what I have
private-route.js
function PrivateRoute({ component: Component, ...rest }) {
const [token, setToken] = useState();
useEffect(() => {
setToken(localStorage.getItem("usr-sess"));
console.log(token);
}, [token]);
return (
<Route
{...rest}
render={props =>
token ? (
<Component {...props} />
) : (
<Redirect to="/login" />
)
}
/>
);
}
export default PrivateRoute;
app.js
<Switch>
<Route exact path="/login" component={LoadableLogin} />
<PrivateRoute eexact path="/test" component={LoadableTestPage} />
<Route component={LoadableNotFound} />
</Switch>
server.tsx
const jsx = renderToString(
<ApolloProvider client={clientConfig}>
<Loadable.Capture report={moduleName => modules.push(moduleName)}>
<StaticRouter context={context} location={req.url}>
<App />
</StaticRouter>
</Loadable.Capture>
</ApolloProvider>
);
I constantly keep being redirected back to login even when the token is there.
The issue is that it takes a bit of time to setToken() and by then it will redirect me back to login. Is there any way to do this?
Not using useEffect and doing
localStorage.getItem("usr-sess") ? (
<Component {...props} />
) : (
<Redirect to="/login" />
)
throws a localStorage undefined error, I can't think of anything else to do.
Related
We're using react 16.13 and react-router-dom 5.1.2 but we struggle to make it work the way we want:
it works great when used with our https://react.domain.com/
but it doesn't when a user starts the app directly on a subpage, https://react.domain.com/page1 as we can't find any way to tell react-router that the base url is still / and not page1/
(We've configured the nodejs/express backend to send the react app no matter the route)
So react-router just adds all the history / path to the /page1 url, resulting in /page1/login, /page1/page1, etc. instead of the /login or /page1 urls.
Is there any way to achieve that?
The goal is to allow the refresh of the page, and to allow direct access to a specific route.
EDIT: code example
client side
<StoreProvider store={store}>
<Suspense fallback={<MainPageFallback />}>
<ThemeProvider theme={theme}>
<Router basename={location.pathname}>
<I18nextProvider i18n={i18next}>
<TryCatch>
<Navbar actions={<UserSettings />} />
<div className={classes.appContent}>
<Switch>
<Route exact path="/start">
<LandingPage />
</Route>
<Route exact path="/search">
<SearchPlacePage />
</Route>
<Route exact path="/signup">
<SignUpPage />
</Route>
<Route exact path="/signin">
<LoginPage />
</Route>
<Route exact path="/forgot_password" component={ForgotPassword} />
<Redirect from="/*" to="/start" />
</Switch>
<Snackbar />
</div>
</TryCatch>
</I18nextProvider>
</Router>
</ThemeProvider>
</Suspense>
</StoreProvider>
server side
#Route('/*')
public async home({ query }: Request, res: Response) {
const { resolve } = await import('path');
const { readFileAsync } = await import('../Infrastructure/readFileAsync');
try {
const file = (await readFileAsync(resolve(__dirname, '..', '..', '..', 'static', 'index.html')))
.toString('utf-8');
return res.send(file);
} catch (err) {
return res.sendFile(resolve(__dirname, '..', '..', '..', 'static', 'notFound.html'));
}
}
You need to use Routes rather than Switch for react-router-dom#6. Something like this:
<BrowserRouter>
<Routes>
<Route path="/" element={<App />}>
</Route>
</Routes>
</BrowserRouter>
I am having trouble with dynamic routing in
react-router-dom.
Whenever I go to the dynamic page I get a white screen. Only error message is
The resource from localhost:8082/individuals/bundle.js was blocked due to MIME type(“text/html”) mismatch (X-Content-Type-Options: nosniff).
I have research countless articles and have been unable to find a solution.
Every Other static Component Works and is able to pull from the api.
The API I am pulling from is able to dynamically display products.
"http://localhost/40"
will display the object with the id of 40 in json format. Node.js express Api
Thank you for your time
Route Component
function Rout(){
return(
<div>
<BrowserRouter>
<Menu/>
<div className="page">
<Routes>
<Route index element={<MainContent />} />
<Route path="/products" element={<Product />} />
<Route path='/individuals/:id' element={<Individualproduct />} />
<Route path="/about/" exact element={<About />} />
<Route path="/shippingandreturns" element={<Shipping />} />
</Routes>
</div>
</BrowserRouter>
</div>
)
}
export default Rout
Dynamic Component
const Individualproduct = () => {
const { id } = useParams();
const [products, setProducts] = useState([]);
const url = 'http://192.168.1.91:4001/';
useEffect(()=>{
async function getProduct(){
const response = await axios.get(`${url}/${id}`);
const a = (response.data.Results)
console.log(a)
setProducts(a)
}
getProduct()
}, [])
return (
<article>
{
products.map((item)=>{
<h1>{item.id}</h1>
})
}
</article>
);
};
I am having a hard time setting up my react-router so that when the path matches the user's username, the userProfile component will be routed and show individually. For example, when the URL is http://localhost:3000/myusername, the userProfile component will only show. I'm kind of confused on how it will work if someone just pastes the entire link instead of being directed to someone's page through the UI.
<Switch>
<Route path="/" component={userProfile} />
<PrivateRoute exact path="/test2" component={test2} />
<PrivateRoute exact path="/test3" component={test3} />
</Switch>
Any help would be appreciated!
You can use this method and access to username in test3 component and handle getting data and showing this page
<Switch>
<Route path="/" component={userProfile} />
<PrivateRoute exact path="/test2" component={test2} />
<PrivateRoute exact path="/:username" component={test3} />
</Switch>
I found that if you just use /:id in the path, then the path extension will pass automatically down to the userProfile component when you use the const {id} = useParams() function in the userProfile component. Then I passed the _id down as a prop from the userProfile component to the Info component so that it can be used to automatically fetch data in a useEffect() and redux dispatch() function. Oh and also, the <Route path="/:id" component={userProfile} /> needs to be an exact path and must go to the bottom of the Route Switch list so that it doesn't get picked first.
<Switch>
<PrivateRoute exact path="/test2" component={test2} />
<PrivateRoute exact path="/test3" component={test3} />
<Route exact path="/:id" component={userProfile} />
</Switch>
const Profile = () => {
const { id } = useParams();
return (
<div>
<Info _id={id} />
</div>
);
};
export default Profile;
const Info = (props) => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(getProfileInfo(props._id));
}, []);
return (
<div></div>
);
};
export default Info;
I'm new to react and I'm trying to make a small program.
I have a react based client, my server is running on Nodejs and my db is Postgres.
I managed to make a working login from the client with auth from the server.
How can I show a new page after a login has been successful?
my app.js:
export default class App extends React.Component {
render() {
const { url } = this.props.match
return (
<div className="fx-row high">
<Sidebar url={ url }/>
<div className="page fx-col fx-grow" style = {{ width : 150, height : 150, marginLeft : 450, marginTop : 100}}>
<Switch>
<Route path={ url + 'demo' } component={ Demo }/>
<Route path={ url + 'audits' } component={ Audits }/>
<Route component={ Login }/>
</Switch>
</div>
</div>
)
}
}
In My login I have a handleSubmit which sends a post with parameters to the server and get the response body.
I want that after handltSubmit get the correct response, to show a different screen from the login, lets say just a screen that says "You are logged in" for now.
Thank you
Use BrowserRouter from react-router-dom.
import {BrowserRouter, Switch, Route} from 'react-router-dom';
export default class App extends React.Component {
render() {
return (
<div className="fx-row high">
<Sidebar url={ url }/>
<div className="page fx-col fx-grow" style = {{ width : 150, height : 150, marginLeft : 450, marginTop : 100}}>
<BrowserRouter>
<Switch>
<Route path='/demo' component={ Demo }/>
<Route path='/audit' component={ Audits }/>
<Route path='/SiteMange' component={ SiteManage }/>
<Route path='/' component={ Login }/>
</Switch>
</BrowserRouter>
</div>
</div>
)
}
}
In your Login component you can call push method of history object that you will get as props in your routed page.
so on login success call the following method to redirect to your next route. For example say home page.
this.props.history.push('SiteMange')
Assuming you're using fetch for server request you could do the following:
handleSubmit = () => {
fetch(your code here)
.then(res => res.json())
.then(data => {
if ( data.user ) {
this.props.history.push("home"); // in this case I use home as your path name
} else {
Some actions here
}
});
};
Hope it helps.
I'm trying to define the routes for a paginated application.
/ -> handled by App
/page/:page -> also handled by App
These are what my routes look like:
var routes = (
<Route name="app" path="/" handler={App}>
<Route name="paginated" path="page/:page" handler={App} />
</Route>
);
This is what App looks like:
var App = React.createClass({
render : function() {
return (
<div>
<RouteHandler/>
Something...
</div>
);
}
});
The problem here is that, as paginated is an app's child route, that Something... in the componet gets rendered twice.
What I'm trying to acomplish here is to default to page 1 for the app route and to load the desired page for the paginated route, without loading twice.
Any way to do this?
Using the App handler twice works the way you expect - it calls Apptwice. However, the parent should only be used as the parentRoute Handler, and the children use their ownHandlers`.
To get the initial page to load properly, use a DefaultRoute for the base path:
routes
var routes = (
<Route name="app" path="/" handler={App}>
<DefaultRoute handler={Home}/>
<Route name="paginated" path="page/:page" handler={Page} />
</Route>
);
app
var App = React.createClass({
render : function() {
return (
<div>
<RouteHandler/>
</div>
);
}
});
home
var Home = React.createClass({
render : function() {
return (
<div>
...Something...
</div>
);
}
});
page
var Page = React.createClass({
render : function() {
return (
<div>
...Something Completely Different...
</div>
);
}
});
The React Router Default Handler Docs have a more substantial example:
<Route path="/" handler={App}>
<!--
When the url is `/`, this route will be active. In other
words, `Home` will be the `<RouteHandler/>` in `App`.
-->
<DefaultRoute handler={Home}/>
<Route name="about" handler={About}/>
<Route name="users" handler={Users}>
<Route name="user" handler={User} path="/user/:id"/>
<!-- when the url is `/users`, this will be active -->
<DefaultRoute name="users-index" handler={UsersIndex}/>
</Route>
</Route>
Notice here that <Route name="user" handler={User} path="/user/:id"/> also has a default route, so when there is no :id match it has somewhere to go.
Hope this helps!
At the end I came up with a solution using a redirection that allowed me to have the pagination and nested routes in the paginated one.
var routes = (
<Route name="app" path="/" handler={App}>
<Redirect from="/" to="paginated" params={{ page : 1 }} />
<Route name="paginated" path="page/:page" handler={PaginationLoader} />
</Route>
);