35

I am using reactjs as a frontend and django as backend. React router is used for routing. When i refresh the page that has routed by react router, i get django 404 Page Not Found error. If i refresh the homepage, i dont get any such error because the homepage is rendered by django template too using its url.

Do i have to configure that in the webpack? My project structure is i have seperated django and reactjs. I have created a folder as frontend where reactjs file resides.

UPDATE

homepage template has all the link for routes like addrestaurant.

my webpack.config file

const path = require("path");
if(!process.env.NODE_ENV) {
    process.env.NODE_ENV = 'development';
}

module.exports = {
  entry: [
    './src/index.js'
  ],
  output: {
    path: path.join("../app/static/build/", "js"),
    filename: "app.js",
    publicPath: "../app/static/build/"
  },
  devtoo: 'source-map',
  debug: true,
  module: {
    loaders: [{
      exclude: /node_modules/,
      loader: 'babel',
      query: {
        presets: ['react', 'es2015', 'stage-1']
      }
    },
    {test: /\.(jpe?g|png|gif|svg)$/i, loader: "url-loader?name=images/[name].[ext]"},
    ]
  },
  resolve: {
    extensions: ['', '.js', '.jsx']
  },
  devServer: {
    historyApiFallback: true,
    contentBase: './'
  }
};

urls.py

urlpatterns = [
    url(r'^', views.home, name="homePage"),
    url(r'^(?:.*)/?$', views.home),
]

home.html

{% extends 'base.html' %}

{% block title %} Foodie | Homepage {% endblock title%}

{% block content %}
  <div class="homepage">
  </div>
{% endblock %}

{% block js %}
  {{ block.super }}
  <script type="text/javascript">
  var data = {
         isUserAuthenticated:{% if request.user.is_authenticated %}true{% else %}false{% endif %}
    };
    console.log('data',data);
    $(function() {
      app.showHomePage(".homepage",data);
    });
  </script>
{% endblock %}

index.js

window.app = {
      showHomePage: function(id,data){
          render(
            <Provider store={createStoreWithMiddleware(reducers)}>
                <Router>
                 <App />
                </Router>
            </Provider>, document.querySelector(id)
          );
      },
}

Banner is a child component of App component

const Banner = (props) => (
   <div className="navbar-container">
        <div className="ui container">
            <div className="ui large secondary menu">
                <a className="toc item">
                    <i className="sidebar icon"></i>
                </a>
                <div className="item logo">
                    <div className="ui logo shape">
                        <div className="sides">
                            <div className="active ui side">
                                Foodie
                            </div>
                        </div>
                    </div>
                </div>
                <Link to="/restaurant" className="active item tab">Home</Link>
                <Link to='/addrestaurant' className='item tab'>Add Restaurant</Link>
                <Link to="/products" className="item tab">Products</Link>
                <div className="right item">
                    <a href="" id="bookingInfoButton" className="ui white inverted button">Booking</a>
                </div>
            </div>
        </div>
      </div>
);

export default Banner;
milan
  • 2,409
  • 2
  • 34
  • 71

6 Answers6

40

The issue is probably that you haven't configured your URLs to handle the routes that are defined in React Router. In your Django urls.py you should be using a catch all to match all URLs to your index template

urlpatterns += [
    # match the root
    url(r'^$', base_view),
    # match all other pages
    url(r'^(?:.*)/?$', base_view),
]

The base_view would be a view function that renders a template which includes your bundled app.

Paul S
  • 35,097
  • 6
  • 41
  • 38
  • Is it still a 404 error or is it an issue with your React app? – Paul S Nov 27 '16 at 17:15
  • my browser got hang. – milan Nov 28 '16 at 01:21
  • I'm not really sure what you mean. Your browser is freezing when you try to load your project? I'm not sure what would cause that unless you have an infinite loop somewhere. You probably need to do some dev tools debugging. Is it at least loading the correct page now? – Paul S Nov 28 '16 at 03:36
  • Hi Paul S, I stumbled upon this question cause I have the same issue. Not really a Django girl, so could you please tell me what should I put in base_view? – Laura Silvani Mar 29 '17 at 12:14
  • @LauraSilvani `base_view` would just be a view function. Something like: `def base_view(request): return render(request, template_name='index.html')`. If you had any initial state for the template, you would pass it using `render`'s `context` kwarg. – Paul S Mar 29 '17 at 14:57
  • 3
    In case you have other URLs in your stack (I use django's built-in login/logout views), make sure you add the catch-all URL at the bottom of your URL stack. – Pedro Walter Jul 21 '17 at 02:15
  • In case someone's wondering, I had the exact problem and this answer solved it. I ended up using a mixture of django's new `path()` and the old `urls()` in my `urls.py`: urlpatterns = [ path('login/', LoginView.as_view(), name='login'), path('logout/', LogoutView.as_view()), path('/', login_required(TemplateView.as_view(template_name="app.html"), login_url='login')), url(r'^(?:.*)/?$', login_required(TemplateView.as_view(template_name="app.html"), login_url='login')), ] – Kevin Martin Jose Oct 02 '18 at 14:04
  • Paul S, it works but the browser do nto see iamges I think it searchs the URLs of img files among the react-router URLs.How I can fix it . if you need more info about my problem I can share the code .Thank you in advance) – Ginger Bread Apr 02 '19 at 20:22
28

In case anyone has this same problem, in django 2.0, follow 'Kevin Martin Jose' answer but instead, replace url with re_path

from django.urls import path, re_path

urlpatterns = [
    path('login/', LoginView.as_view(), name='login'),
    path('logout/', LogoutView.as_view()),
    path('/', login_required(TemplateView.as_view(template_name="app.html"), 
    login_url='login')),
    re_path(r'^(?:.*)/?$', login_required(TemplateView.as_view(template_name="app.html"), 
    login_url='login')),
]
OBAA
  • 425
  • 4
  • 6
  • 1
    Thank you! This really worked. I had 2 separate django apps one for the django-rest and the other for the react. I only needed to place the the `re_path` in the frontend app (the react part) in my urls. – Mikebarson Mar 20 '19 at 08:19
  • My case as well. Of course, just in case, the frontend's `urls.py`'s urls should be included last in your main app's `urls.py` or else we're going to swallow API requests and return app.html for them instead of their actual responses :D – Sebastián Vansteenkiste Oct 11 '19 at 14:22
7

In case someone's wondering, I had the exact problem and Paul S's answer solved it. Adding an answer instead of comment only because SO does not let me format code snippets inside comments. I ended up using a mixture of django's new path() and the old urls() in my urls.py:

urlpatterns = [
    path('login/', LoginView.as_view(), name='login'),
    path('logout/', LogoutView.as_view()),
    path('/', login_required(TemplateView.as_view(template_name="app.html"), 
    login_url='login')),
    url(r'^(?:.*)/?$',    login_required(TemplateView.as_view(template_name="app.html"), 
    login_url='login')),
]

Django handles the login, logout and the root /. React router handles everything else

4

Here is an answer inspired by the Kevin Jones and Paul S answers. I was having issues with the regex and hosting a REST API. If my front-end app ever didn't append the slash when making API calls, it wouldn't match and it would get routed back to the front end. This is because the django setting APPEND_SLASH=True requires that it goes through urlpatterns and fails once before it appends the slash and tries again. Therefor, here is a regex that just excludes anything starting with 'api' or 'admin' and otherwise sends it on to the front end.

urlpatterns = [
path("admin/", admin.site.urls),
path("api/", include(router.urls)),  # from rest_framework
re_path('(^(?!(api|admin)).*$)',
    TemplateView.as_view(template_name="build/index.html")),
] 
t1m0
  • 735
  • 1
  • 7
  • 14
1

Here's a solution that does not cause overrides of other views, and doesn't require you to trash your 404 handler.

The disadvantage is this requires keeping an up-to-date list of your routes, but that's what this file is meant for.

In urls.py:

from django.urls import re_path
from myapp import views

# This wrapper turns your list into a regex URL matcher
react_views_regex = r'\/|\b'.join([

    # List all your react routes here
    'view1',
    'view2'

]) + r'\/'

urlpatterns = [
    # Change views.index to whatever your view is
    re_path(react_views_regex, views.index),
]

These URLs should work with or without a trailing slash, are case-sensitive (like real routes), and will only match the full word (so views like 'user' and 'users' will not conflict).

Preston Badeer
  • 2,658
  • 1
  • 20
  • 21
0

Use HashRouter instead of BrowserRouter or manually type react-router-dom path in Django application (app) urls.

sanbinary
  • 81
  • 1
  • 8
  • Can you explain why it works? – kelsny Nov 14 '22 at 20:21
  • This happens between server and client side, in this case you specify the path on client side using react router, it actually works using window.location.pathname. you reload the page the server checks the path exists on the server side, that's why you have. There is no page error, but you are using HashRouter and the server does not know the contents of the path after the hashtag. refer: https://stackoverflow.com/a/51976069/18568151 – sanbinary Nov 15 '22 at 04:48