Liondancer
Liondancer

Reputation: 16469

404 page not found using Django + react-router

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.

Upvotes: 7

Views: 4611

Answers (5)

Myzel394
Myzel394

Reputation: 1317

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'))
]

Upvotes: 1

Kostas Mouratidis
Kostas Mouratidis

Reputation: 1265

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...

Upvotes: 2

Rik Schoonbeek
Rik Schoonbeek

Reputation: 4460

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'))
    ]

Upvotes: 0

ramusus
ramusus

Reputation: 8315

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.

Upvotes: 8

Jesvin Jose
Jesvin Jose

Reputation: 23088

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')

Upvotes: 1

Related Questions