25
votes

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;
5
My project structure is same as yours. I didn't have to do any kind of extra url configuration anywhere. Can you share the file where you have defined your routes. And also the file where you have defined the django url - Swapnil
django routes or reactjs? - milan
Your react router is not having a path. Last time I used React router, I had to specify path attribute. Is this a new way of using react router? - Swapnil
its a react router v4 - milan
Add url(r'^(?:.*)/?$', base_view), in urls.py file. - NinjaDev

5 Answers

31
votes

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.

22
votes

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')),
]
5
votes

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

1
votes

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")),
] 
0
votes

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