4

As the title asks, is there a recommended method for using:

username.domain.com/event/id/

Where I can ask React to reference both id and username as parameters?

As of now

Currently I can obtain id fine using React router (or any parameter after the domain name in the url).

App.js

import { BrowserRouter as Router, Route, Routes } from "react-router-dom";


<Router>
    <Routes>
        <Route path="/event/:id/"
            element={<>
                <EventComponent />
            </>}
        />
    </Routes>
</Router>

EventComponent.jsx

import { useParams } from 'react-router-dom';
let params = useParams();
let eventID = params.id;
// I can now use eventID to reference what was in the url

I can catch server subdomains in my nginx.conf. This is what I use to reach my React app (not a wildcard subdomain yet, but that's not much of a change and not the problem):

server {

    listen      443 ssl;
    charset utf-8;
    client_max_body_size 10M;

    server_name         domain.com;
    
    root                "/usr/share/nginx/html/domain.com/public_html";

    location / {
        index            index.html;
        try_files $uri $uri/ /index.html?/$request_uri;
    }

    location /sw.js {
        add_header Cache-Control "no-cache";
        proxy_cache_bypass $http_pragma;
        proxy_cache_revalidate on;
        expires off;
        access_log off;
    }
    
}

Problem

But I don't know a clean way to pass subdomains to React without rewriting the url in nginx so that the subdomain is becomes part of the url after the domain. This is not a scenario that's desired.

(the following is an edit added to be more specific about the problem:)

I'm aware of getting the subdomain from window.location.hostname and splitting it to obtain a string, but I'm seeking clarity on whether this (or another method) is the most desired and won't have caveats that can be better avoided.

To illustrate my hestitation, I could also just get the id parameter from window.location, but I don't, as it appears to be typical to use useParams in react to do so. I'm always learning and, in doing so, try not to pick up bad habits.

Relevant criteria

As mentioned, I don't want to rewrite the domain from the above url into something like domain.com/event/id/username/

The structure of the url and presenting it as the original, to the user, is important.

Secondly, the subdomain is a wildcard. It won't be a fixed string. I can process this fine in nginx but it's important that a solution allows for a dynamic string.

biscuitstack
  • 11,591
  • 1
  • 26
  • 41
  • Wouldnt cookies 'work'? if nginx sets a regex variable from the subdomain and sets a cookie that is fetched in react similar to this https://stackoverflow.com/a/51111771/7120073 sorry i dont mean to polute your thread, just legitimately wondering and trying to help – OldFart Dec 07 '22 at 11:37
  • Server Side Includes? – OldFart Dec 07 '22 at 11:48

2 Answers2

3

A subdomain is something only the Webserver should handle.
React router is not able to distinguish between them.

There are multiple ways of achieving the desired outcome, I'll share my thoughts based on previous experiences.

Note: This assumes all the subdomains should point to the React index.html


Native + Props

Since all the subdomains will trigger the same React app, we can use the regular Javascript way of retrieving the subdomain, this can be passed to deeper components as a normal prop.

# App.js
const subdomain = // prefered way of retreiving subdomain;
<EventComponent subdomain={subdomain} />

# EventComponent
let params = useParams();
let eventID = params.id,
    subdomn = props.subdomain

Query Parameter

(I've used this in production to move a header to a query parameter that is removed instantly so the user can't see it, works great)
You can let Nginx add a query parameter with the subdomain (../?user=foobar), this can easily be parsed with Javascript, to ensure the user does not notice this, you can remove the param after the page loads, the user won't even notice

server_name  ~^(?<subdomain>.+)\.example\.com$; 
return       301 http://example.com$request_uri?user=$subdomain;

Cookie

The same idea as above, but instead off using query parameters, you can inject the subdomain into a cookie and read it using Javascript

0stone0
  • 34,288
  • 4
  • 39
  • 64
1

If username.domain.com is the public url, I don't quite see why you have to pass this to react, surely you can just interrogate the domain name from the browser directly.

If not, please add info with an example of the public and local (to nginx) domains and maybe elaborate on some of the reverse proxy rules and I'll remove the answer / attempt to update.

EventComponent.jsx

import { useParams } from 'react-router-dom'
const params = useParams()
const eventID = params.id
const username = window.location.hostname.split('.')[0]

Working example of using this method in React:

const Link = ReactRouterDOM.Link
const Route = ReactRouterDOM.Route

class ArticleComponent extends React.Component {
  constructor(props) {
    super(props)
    this.hostname = window.location.hostname
    this.username = window.location.hostname.split('.')[0]
  }
  render() {
    return (
      <div>
        <p>Username fetched from parent App and passed as prop in the standard React manner</p>
        <p><i>Note: It will say `stacksnippets` as the username, because there are only two levels in the hostname. If there were 3 levels eg, `user1.domain.com`, the username would be `user1`</i></p>
        <p>Hostname: <b>{this.hostname}</b></p>
        <p>Username via props: <b>{this.props.username}</b></p>
        <p>Username from component directly: <b>{this.username}</b></p>
        <p>Article ID: <b>{this.props.match.params.id}</b></p>
      </div>
    )
  }
}

class App extends React.Component {
  constructor(props) {
    super(props)
    this.props.username = window.location.hostname.split('.')[0]
  }
  render() {
    return (
      <ReactRouterDOM.HashRouter>
        <ul>
          <li><Link to="/">TO HOME</Link></li>
          <li><Link to="/articles/link1">TO /articles/link1</Link></li>
          <li><Link to="/articles/link2">TO /articles/link2</Link></li>
        </ul>
        <Route path="/" exact component={Home} />
        <Route path="/articles/:id" render={(props) => (<ArticleComponent username={this.props.username} {...props}/>)} />
      </ReactRouterDOM.HashRouter>
    )
  }
}

const Home = props => <div><h1>HOME</h1><p>Click a link to navigate</p></div>

ReactDOM.render(<App />, document.querySelector('#root'))
<script src='https://unpkg.com/react@16.3.1/umd/react.production.min.js'></script>
<script src='https://unpkg.com/react-dom@16.3.1/umd/react-dom.production.min.js'></script>
<script src='https://unpkg.com/react-router-dom@5.0.0/umd/react-router-dom.min.js'></script>
<script src='https://unpkg.com/babel-standalone@6.26.0/babel.js'></script>
<div id='root'></div>
dangarfield
  • 2,210
  • 1
  • 13
  • 18
  • Perhaps obtaining the subdomain straight from the url via `window.location.hostname` is the best method. I am not finding much written about this in the context of the question I'm asking, so I'm seeking clarification. For example, I could similarly obtain id from the url via `window` but this is not typically the recommended method. – biscuitstack Dec 07 '22 at 19:28
  • I'll add an example in a few minutes – dangarfield Dec 07 '22 at 19:37
  • If you mean an example of `window.location.hostname` to obtain the subdomain string, there's no need, I'm familiar. Or perhaps i misunderstood your last comment – biscuitstack Dec 07 '22 at 19:56
  • Apologies, I got distracted, example added. If relevant, great. If not, that's fine too. – dangarfield Dec 07 '22 at 20:21
  • Thanks. I was familiar with this method already. I think the confusion came from my wording of 'much written about this IN THE CONTEXT OF', sorry. It wasn't to imply I wasn't familiar with this method, just its suitability to avoid future issues in the context I asked. I wasn't seeing anywhere that using `window.location.hostname` was best practice in this stack flow and was looking for clarification on best practices to get from A -> B. This was written in my bounty but not worded as specifically in my original question, my bad. – biscuitstack Dec 08 '22 at 10:06