I am new to programming which makes things slightly difficult for me to understand if I read the official docs.
I was reading about React Router 4 from here
In this article, the author was talking about <HashRouter> and <BrowserRouter>
This is what he mentioned:
HashRouter basically it uses the hash in the URL to render the component. Since I was building a static one-page website, I needed to use this.
BrowserRouter, it uses HTML5 history API to render the component. The history can be modified via pushState and replaceState. More information can be found here
Now, I don't get the significance and use cases for both, Like what does he mean when he says history can be modified via pushState and replaceState and it uses the hash in the URL to render the component
While the first explanation for BrowserRouter is entirely vague to me, the second explanation about HashRouter also doesn't make sense, like why would someone use Hash (#) in the url to render the component?
BrowserRouter
It uses history API, i.e. it's unavailable for legacy browsers (IE 9 and lower and contemporaries). Client-side React application is able to maintain clean routes like example.com/react/route but needs to be backed by web server. Usually this means that web server should be configured for single-page application, i.e. same index.html is served for /react/route path or any other route on server side. On client side, window.location.pathname is parsed by React router. React router renders a component that it was configured to render for /react/route.
Additionally, the setup may involve server-side rendering, index.html may contain rendered components or data that are specific to current route.
HashRouter
It uses URL hash, it puts no limitations on supported browsers or web server. Server-side routing is independent from client-side routing.
Backward-compatible single-page application can use it as example.com/#/react/route. The setup cannot be backed up by server-side rendering because it's / path that is served on server side, #/react/route URL hash cannot be read from server side. On client side, window.location.hash is parsed by React router. React router renders a component that it was configured to render for /react/route, similarly to BrowserRouter.
Most importantly, HashRouter use cases aren't limited to SPA. A website may have legacy or search engine-friendly server-side routing, while React application may be a widget that maintains its state in URL like example.com/server/side/route#/react/route. Some page that contains React application is served on server side for /server/side/route, then on client side React router renders a component that it was configured to render for /react/route, similarly to previous scenario.
SERVER SIDE: HashRouter uses a hash symbol in the URL, which has the effect of all subsequent URL path content being ignored in the server request (ie you send "www.mywebsite.com/#/person/john" the server gets "www.mywebsite.com". As a result the server will return the pre # URL response, and then the post # path will be handled (or parsed) by your client side react application.
CLIENT SIDE: BrowserRouter will not append the # symbol to your URL, however will create issues when you try to link to a page or reload a page. If the explicit route exists in your client react app, but not on your server, reloading and linking(anything that hits the server directly) will return 404 not found errors.
Refreshing the page causes the browser to send a GET request to the server using the current route.
The # was used to prevent us from sending that GET request. We use the BrowserRouter because we
want the GET request to be sent to the server. In order to render the router on the server, we need a
location — we need the route. This route will be used on the server to tell the router what to render.
The BrowserRouter is used when you want to render routes isomorphically.
Both BrowserRouter and HashRouter components were introduced in React Router ver.4 as subclasses of Router class. Simply, BrowserRouter syncs the UI with the current URL in your browser, This is done by the means of HTML-5 History API. On the other hand, HashRouter uses the Hash part of your URL to sync.
One more use case i want to add.
While using BrowserRouter or Router, it will work fine on our node server. Because its understand client routing (Preconfigured).
But while we deploy our build React app on Apache server(just say PHP, on GoDaddy), then routing will not work as expected. It will land into 404, for that we need to configure .htaccess file. After that also for me each click/url, its sending request to server.
In that case better we use HASH Routing (#). # we use this on our html page, for traversing in HTML content and it will not lead to server request.
In above scenario we can use HashRouting, that is all url that present after #, we can put our routing rules to work as SPA.
"Use Cases"
HashRouter: When we have small client side applications which doesn't need backend we can use HashRouter because when we use hashes in the URL/location bar browser doesn't make a server request.
BrowserRouter: When we have big production-ready applications which serve backend, it is recommended to use <BrowserRouter>.
Reference by book: Learning React: Functional Web Development with React and Redux
By Alex Banks, Eve Porcello
The main use case scenario for choosing "hash router" over "browser router" is on production environment. Let's say we have this url "www.example.com/Example". In this case, some servers tend to search a folder by a name "Example" and ultimately return 404 as we have single page application, index.html. So, to avoid such confusion we use "www.example.com/#/Example". That is where hash router shines.
resource: https://www.techblogsnews.com/post/browser-router-vs-hash-router-in-react-js
Comment BrowserRouter vs Router with history.push() refers to the article https://www.techiediaries.com/react/react-router-5-4-tutorial-examples/#React_Router_5_Routers_BrowserRouter_vs_HashRouter
“If you are using a dynamic server that can handle dynamic URLs then
you need to use the BrowserRouter component but if you are using a
server that only serves static files then a HashRouter component is
what to be used in this cases”
Limitation of HashRouter re history
I previously misinterpreted a note at the top of HashRouter article in https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/docs/api/HashRouter.md
As this technique is only intended to support legacy browsers, we
encourage you to configure your server to work with BrowserHistory
instead.
as discouraging to use HashRouter in general, but authors do not recommend HashRouter only in case if if you need support for location.key or location.state.
Basically if one uses BrowserRouter one can use a url like "soAndSoReactPage/:id"
eg:
with a Route <Route path="soAndSoReactPage/:id"><soAndSoReactComponent/></Route>
now, in the react component "soAndSoReactComponent"
the "id" can then be extracted using useParams and thus you can display the page "soAndSoReactComponent" as per the id
such a thing can't be done with HashedRouter,
Related
I started a Django app and i created the whole authentication layer using Django-Allauth, it already has quite some features such as email confirmation, password reset and two factor authentication. Now i realized that, since my app will be heavily interactive and with a lot of real time features, i'm going to need a Vue SPA to use with Django, so i'm thinking of creating the Vue SPA on the same server and domain and have Django as a Rest API.
Here is my problem: since i already made the whole authentication part using Django templates and Django urls, if i separate the rest of the frontend from the backend, will i have to rewrite everything? Is it possible to have a Vue app and a Django backend app separated on the same domain where authentication is handled by Django templates and all the rest is a Vue app with vue routes and all the other interactions are handled by Django Rest Framework endpoints?
So maybe something like this:
urlpatterns = [
path('accounts/signup/', SignUpView.as_view(), name='signup'), #Django template
path('accounts/login/', LoginView.as_view(), name='login'), #Django template
...
]
And these are the only Django-handled urls where the page is rendered by Django views. Once the user is logged in, they will be redirected to the VueJS app.
My personal opinion, it's not worth it to keep a bunch of server side pages just for sign-up, login, ... Managing both server-side pages and front-end pages in long run is a headache. But if you like that way, here are my suggestions.
For authentication, use Django auth. No matter if it's a server side HTML page or it's an API end-point. Django auth is simple and secure. Don't roll your own auth, don't store tokens in localstorage or so.
Fully separate these 3:
Front-end URLs (i.e. routes stored in Vue)
Back-end page URLs (i.e. HTML pages severd by Django)
Back-end API end-points URLs (i.e. the ones user never see, only Vue uses them under the hood)
They can be on separated domains but it can be just by a path prefix as well. As you yourself suggested in a comment.
Now when user visits some page in BE, it will use server side rendering and every user click is a browser refresh. Until you hit a FE URLs, then your front proxy should redirect user to FE or you'll serve JS files directly from Django. After that every user click is handled inside Vue without a refresh. If user hits a URL prefix that's for BE, then FE needs to do something like document.location = "/server-side/some-page.
BTW few days ago I answered another question that was similar to this, maybe you find the answer or comments there useful.
So in order to log in from the SPA i need to send a csrf token, and in order to get the token i can create a Django view that returns a CSRF token to the user so that it can be used to login. Wouldn't it provide attackers a way to attack my server (stackoverflow.com/questions/43567052/…)
My suggestion is to turn CSRF protection off and instead make session cookie samesite=Lax (I think that's default in newer versions of Django). All major browsers support this and it prevents CSRF.
Otherwise you can read token from another API or from cookie like here
So on production i will use Nginx to have the Vue app and the Django backend app on the same server and domain, but on development how can i do that? If i run the two apps on different terminals, won't django consider the Vue app to be in a different server?
It can't understand what server it is. The only thing you should care is the domain of the cookie. It should be set on your BE domain. When running on local both FE and BE are running on domain "localhost" so there should be no issue.
I'm working on a Next.js app that needs to fetch a config file from a remote server before initializing. I want to request the config file just once per call to the server before rendering the app server side. After that, I would like to be able to get the same config in the client without having to make a second request to the remote server from the browser.
I have tried to achieve this by using getInitialProps function either in _app or _document files and then use the React's Context API to make the configuration visible to every component but unless I'm wrong, this will run the code that requests the configuration both in server (on the first call from the browser) and client (on every page navigation).
I have also tried to create a server.js file, request the configuration from there and store in a variable within a ES6 module. However, I couldn't make this approach work because apparently the Next.js React app can't access the same modules than the server.js because they are actually two different apps. Again, I could be wrong.
Basically I would like to know if Next.js offers any kind of "bootstrapping place" where I can perform app initialization tasks that generate data that can be sent to the React app Next.js will initiate.
You are correct in that anything in getInitialProps in _app.js or _document.js will be run on every server request. Once you get your config, you can pass it down to the components in pages as props. From there, I think you have two choices:
Now that the app is bootstrapped, run the rest of the app as a SPA client-side. This would prevent any future SSR from happening. Obviously, you lose the benefits of SSR and the initial page load is likely longer, but then it would be snappy afterwards.
After getting the config in _app.js, send it as a cookie (assuming it's not too big to be a cookie?). On future requests to the server, the cookie will be sent automatically and you would check the cookie first - if it doesn't exist, get the config. If it does exist, skip that more expensive bootstrapping because the app is bootstrapped.
So I think it really depends on whether you want a single page application bootstrapped on the server but then entirely client side after that (option 1) or server side rendering per page while minimizing expensive bootstrapping (option 2).
Nothing prevents you from sending multiple cookies from the server if that makes sense for your app and bootstrapping. And remember not to make it a HTTP-Only cookie because you'll want to read that cookie client side - after all, that's what you're looking for - the client side configuration generated on the server.
Despite we ended up leaving Next.js for this and other reasons that made us decide Next.js was not the best option for our use case, we kind of mitigated this problem as follows while be sticked to it, I hope it makes sense to anyone. Also, by now maybe Next.js provides a better way to do this so I would read the Next.js docs before using my approach. Final note: I don't have access to the code anymore since I change to a different company so maybe there are some points that won't be 100% as we made it.
There is goes:
We created a module that was responsible to request the config file and keep the results in a variable. At the moment of importing this module, we ensure that the variable is not already present in window.__NEXT_DATA__. If it is, we recover it, if it's not, we request it to the remote server (this will be helpful in the clint side rendering).
We created a server.js file as described by Next.js docs. In this file we make the call to get the config file and store it in memory.
In the body of the function passed to createServer we add the config file into the req object to make it accesible to the app in the getInitialProps functions server side.
We made sure that the getInitialProps using the config file returns it, so that it will be passed to the components as props and also serialized by Next.js and made available to the client in the __NEXT_DATA__ global Javascript variable.
Given that the config ended up in the __NEXT_DATA__ variable in the server, using the trick described in the step 1 makes the app not request the config for a second time.
I'm trying to setup a protected route (homepage in my case) with React. It works fine on the client. If not authenticated by server, it reroutes to login page. But technically, unauthenticated users can still check out the static content on the protected route (though of course the api calls to server are safe), just by either sifting through code, or by setting state in dev tools. I don't like this.
TLDR question: How can I make sure even the static content in protected routes are not seen by unauthenticated users?
I understand that authentication has to move from client to the server. But what is the proper way to do this with React/React Router?
My ideas:
-Serve an unauthenticated react app/index.html for just Login. When authenticated, serve another app for user content/pages.
-Maybe it's possible to do some component lazy loading from the server that also checks authentication on the request?
My context: create-react-app, express/node backend, using okta auth.
Thank you.
There are a couple of ways around this using a variety of methods.
First is to server-side render everything with a framework like Next.js. This framework is used by a ton of large enterprise companies because of the search engine friendliness of SSR pages. In your scenario though, it would solve your problem of showing content only when someone is authorized.
However, in most React.js apps, your data is coming from a data source such as MongoDB that is hidden behind your backend. The only code/information an unauthorized user would be able to see in your JS is the general layout of pages.
You can make hoc which will wrap your protected component and check if he is authenticated by a server. If not redirect him to homepage or somewhere else.
I may have a basic misunderstanding of how things work, so bear with me.
Project
I have built a small website with NPM, Webpack, React, and react-router. the site has six content-only pages generated by react components. Each page is served up through react router, meaning I can go to localhost:8080/page-2 directly and be given the page I mean to display at that page.
Issue
It works well, but the problem is I can only get it to work while running a server locally, set up using the technique described here
I tried running the site with http-server to see if I could pass my project off to my client to run on whatever server he runs this on. This needs to be a pretty hands-off launch, but my fear is that my client's server needs to be of a certain type, or need to be configured in a specific way.
I understand I may have built this incorrectly. Is there a good way to build React(with react-router) sites in the future so that launching is a simple drag-and-drop process?
Issue
The thing causing your issue is the react-router. If you don't use any routing, the server gives you index.html and then with react (javascript) you handle any static or dynamic content you wish.
When you use react-router for routing, it pushes new URL into your browser every time a link is clicked. The server has no idea that you run a client(browser) app and tries to go into this url/directory specified.
That means that any form of launch you choose, it must either take care of it or you must specify to the server that it needs to serve index.html, whatever the current url is. There are many ways to do that, for example in my Node app, I have:
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, 'index.html'));
});
Summary
The server doesn't know it's a client app. It tries to serve you files/urls that doesn't exist. You must tell it to serve index.html at all times.
I am trying to build a webapp using React, React Router and Webpack libraries, and taking advantage of server side rendering.
Now, I have a basic structure working - when I visit a URL, the page is rendered on the server, sent back to the client where React confirms that the output rendered on the client side matches with what's generated on the server and takes over from there.
My problem is when I try to open URLs which have to load components asynchronously.
When I visit a URL which has to load a dependency asynchronously, the server renders the output including all dependencies, but when it comes to the client, the markup which React renders does not include the asynchronously loaded components and therefore is different from server's and I get an error in the console which says
Warning: React attempted to reuse markup in a container but the checksum was invalid.
The way I see it the solution would be to either not render the asynchronously loaded components on server and lose out on the benefits of server side rendering or to somehow tell React to ignore the differences between the server and client, and not discard the server's markup, because I know that the component will eventually be loaded and markups will match.
Has anyone else come across this problem and knows what a good solution would be?
You need to also run match on the client side to load all the route components.
There's a fleshed out example available at https://github.com/rackt/example-react-router-server-rendering-lazy-routes.