7

I am trying to use reactjs and react-router (1.x) with my Django application but I am having a hard time putting all this together. Here is the github project just incase I dont provide enough information within this question.

https://github.com/liondancer/django-cherngloong

I created a path="about" within my routes.js

var routes = (
    <Router>
        <Route path="/" component={ Views.Layout }>
            <IndexRoute component={ Views.Index } />
            <Route path="about" component={ Views.About } />
        </Route>
        <Route path="*" component={ Views.RouteNotFound } />
    </Router>
);

export default routes;

My layout.js

class Layout extends React.Component {
    constructor(props) {
        super(props);
    }
    render() {
        return (
            <div id="review-web">
                <header className="header">
                    <LogoElement />
                    <CenterPiece />
                </header>
                <div>
                    { React.cloneElement(this.props.children, { path: this.props.path }) }
                </div>
                <Footer />
            </div>
        );
    }
}

export default Layout;

When I enter in localhost.8000/about I get a 404 Django error

enter image description here

My goal is to keep the frontend and backend separate so I believe I should be able to use Django as just an endpoint for data and not for rendering views.

Liondancer
  • 15,721
  • 51
  • 149
  • 255
  • when you go to "/" does it successfully serve the Views.Layout page? To me it looks like your application does not know to server or look at routes.js. Did you configure your app to use routes.js? – SilentDev Oct 19 '15 at 20:28
  • @user2719875 Right now my application uses [this](https://github.com/liondancer/django-cherngloong/blob/master/assets/js/index.js) to use `route.js`. If I were to enter `localhost:8000/` it will display [this](https://github.com/liondancer/django-cherngloong/blob/master/assets/js/views/index.js) component] – Liondancer Oct 19 '15 at 21:21
  • does it work if you do: (see the slash I used at the beginning of the path). Edit: Also, are you sure you don't want to include this line "" on it's own? It's currently nested inside the other route. – SilentDev Oct 19 '15 at 21:24
  • 1
    You'll need to split your route handling in the Django app - e.g. serve anything prefixed by `/api/*` with Django directly, and anything else (wildcard) to `/path/to/your/index.html` - which is your React app. Otherwise Django interprets the `/about` route as it's own - it doesn't talk to the React app whatsoever (it can't). – elithrar Oct 20 '15 at 03:03
  • @elithrar nice idea! but in case of wildcard * server will return http status_code 200 for every request, but it's nice to return http status_code 404 for non existed urls. – ramusus Mar 22 '16 at 10:55

5 Answers5

8

I faced with the same issue and ended up with this solution.

settings.py:

REACT_ROUTES = [
    'login',
    'signup',
    'password-reset',
    'password-change',
    'about',
    ...
]

urls.py:

routes = getattr(settings, 'REACT_ROUTES', [])
urlpatterns = [
    ...
    url(r'^(%s)?$' % '|'.join(routes), TemplateView.as_view(template_name='index.html')),
]

I don't like to duplicate all react-routes in django settings, but if we put everything there url(r'^.*$'), server will always return HTTP status_code 200. And this is only one way to be able return valid HTTP status_code 404 for non existed urls.

ramusus
  • 7,789
  • 5
  • 38
  • 45
  • Can someone translate the 'url' line to a Django 2(+) 'path'? My understanding is currently too limited to do it. Although I will look at it now, and update the answer when I solve it. – Rik Schoonbeek Sep 18 '18 at 11:45
  • hi thanks for your post, i just want to ask is there anyway can make 404 error being handled by react-router side? from my understanding the unmatched routes still being by django.. – luvwinnie Dec 18 '18 at 02:44
  • In Django 2+ url is now deprecated you can use re_path – Tafadzwa L Nyamukapa Mar 12 '21 at 21:34
2

I was inspired by ramusus' answer. I'm using the (I think) newer django.urls.re_path. I'm a bit skeptic about this comment and if it would be better or worse if the 404 was handled by the frontend. This is how I implemented it:

frontend/views.py

from django.shortcuts import render

def index(request):
    return render(request, 'frontend/index.html')

frontend/urls.py

from django.urls import re_path

# Catch all pattern
urlpatterns = [
    re_path('.*/', views.index, name='index'),
]

main/urls.py

from django.contrib import admin
from django.urls import path, include
from rest_framework.authtoken import views


urlpatterns = [
    path('admin/', admin.site.urls),
    ...,
    path('api-auth/', include('rest_framework.urls')),
    path('', include('some_apps.urls')),
    ...,
    # Put this at the end! This way, Django tries all 
    # its API URLs and if it doesn't find anything it
    # redirects to the frontend
    path('', include('frontend.urls'))
]

react front-end

// stuff ...

const Page404 = () => {
  return (
    <h3>404 - Not found</h3>
  );
};

class App extends Component {
    render(){
        return (
            <Router>
                <Navbar />

                <Switch>
                    // React handles any paths you specify, e.g. within the switch
                    <Route exact path='/some_paths/' component={SomeComponents} />
                    // If nothing matches...
                    <Route component={Page404} />
                </Switch>
            </Router>
        )
    }
}

// stuff...
Kostas Mouratidis
  • 1,145
  • 2
  • 17
  • 30
1

My app serves the same client JS for all valid routes. Please excuse the Flask code.

@blueprint.route('/stuff/<path:path>', methods=["get"])
@blueprint.route('/', methods=["get"])
def index(path=None):
    return render_template('app.html')
Jesvin Jose
  • 22,498
  • 32
  • 109
  • 202
1

I could resolve this by using HashRouter in the React app. This adds a # to the beginning of the url and you can use an empty path in the Django urls.py (your path would look like this: example.com/#/about/)

Here is a good explanation (Don't forget to upvote).

Your files would look like this:

App.tsx

<Router>
    <Switch>
        <Route path="/article/:articleID">
            <Article />
        </Route>
        {/* your routes */}
    </Switch>
</Router>

And then simply an empty path. urls.py

urlpatterns = [
    path("", TemplateView.as_view(template_name='index.html'))
]
Myzel394
  • 1,155
  • 3
  • 16
  • 40
0

Based on ramusus answer, this is how I've now fixed it.

As url doesn't work with Django 2 and up anymore. And ramuses answer also seems to be using Python 2, looking at the string formatting. This is using Python 3(.6).

(Although I am sure that there is a more elegant way of doing this, but it works and helps me get a step further in getting my project up and running.)

settings.py: (as ramusus suggested)

REACT_ROUTES = [
    'login',
    'signup',
    'password-reset',
    'password-change',
    'about',
    ...
]

in main urls.py: (partly as ramusus suggested)

from django.conf import settings

react_routes = getattr(settings, 'REACT_ROUTES', [])

for route in react_routes:
    urlpatterns += [
        path('{}'.format(route), TemplateView.as_view(template_name='index.html'))
    ]
Rik Schoonbeek
  • 3,908
  • 2
  • 25
  • 43
  • You can use `django.urls.re_path` in Django 2 as a replacement for `django.conf.urls.url`: https://docs.djangoproject.com/en/2.1/ref/urls/#re-path – Peter Hull Dec 28 '18 at 02:01