React, React Query, and Django
Introduction:
In this tutorial, we’ll walk through creating a form that lets users select a state, county, and town using React, React Query, and Django as a backend. React will be used for building the frontend user interface, React Query for fetching and managing server-side data, and Django to create the backend API.
Prerequisites:
- Basic understanding of React, React Query, and Django
- Node.js and npm/yarn installed on your system
- Python and Django installed on your system
- Django Rest Framework ( install with pip )
Step 1: Setting up the Django Backend
1.1. Create a new Django project and app:
$ django-admin startproject myproject
$ cd myproject
$ python manage.py startapp myapp
1.2. In myapp/models.py
, create the State, County, and Town models:
from django.db import models
class State(models.Model):
name = models.CharField(max_length=100)
class County(models.Model):
name = models.CharField(max_length=100)
state = models.ForeignKey(State, on_delete=models.CASCADE)
class Town(models.Model):
name = models.CharField(max_length=100)
county = models.ForeignKey(County, on_delete=models.CASCADE)
1.3. Add ‘myapp’ to the INSTALLED_APPS
in myproject/settings.py
.
1.4. Run migrations to create the database schema:
$ python manage.py makemigrations
$ python manage.py migrate
1.5. Create serializers for the models in myapp/serializers.py
:
from rest_framework import serializers
from .models import State, County, Town
class StateSerializer(serializers.ModelSerializer):
class Meta:
model = State
fields = '__all__'
class CountySerializer(serializers.ModelSerializer):
class Meta:
model = County
fields = '__all__'
class TownSerializer(serializers.ModelSerializer):
class Meta:
model = Town
fields = '__all__'
1.6. Create views in myapp/views.py
:
from rest_framework import generics
from .models import State, County, Town
from .serializers import StateSerializer, CountySerializer, TownSerializer
class StateList(generics.ListAPIView):
queryset = State.objects.all()
serializer_class = StateSerializer
class CountyList(generics.ListAPIView):
serializer_class = CountySerializer
def get_queryset(self):
state_id = self.kwargs['state_id']
return County.objects.filter(state_id=state_id)
class TownList(generics.ListAPIView):
serializer_class = TownSerializer
def get_queryset(self):
county_id = self.kwargs['county_id']
return Town.objects.filter(county_id=county_id)
1.7. Configure the API URLs in myapp/urls.py
:
from django.urls import path
from . import views
urlpatterns = [
path('states/', views.StateList.as_view(), name='state_list'),
path('states/<int:state_id>/counties/', views.CountyList.as_view(), name='county_list'),
path('counties/<int:county_id>/towns/', views.TownList.as_view(), name='town_list'),
]
1.8. Include the app URLs in the project’s urls.py
:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('myapp.urls')),
]
Step 2: Setting up the React Frontend
2.1. Create a new React project using Create React App:
$ npx create-react-app myapp-frontend
$ cd myapp-frontend
2.2. Install the necessary dependencies:
$ npm install axios react-query
2.3. Replace the contents of src/App.js
with the following:
import React, { useState } from "react";
import { useQuery } from "react-query";
import axios from "axios";
const fetchStates = async () => {
const { data } = await axios.get("/api/states/");
return data;
};
const fetchCounties = async (stateId) => {
const { data } = await axios.get(`/api/states/${stateId}/counties/`);
return data;
};
const fetchTowns = async (countyId) => {
const { data } = await axios.get(`/api/counties/${countyId}/towns/`);
return data;
};
function App() {
const [selectedState, setSelectedState] = useState(null);
const [selectedCounty, setSelectedCounty] = useState(null);
const { data: states } = useQuery("states", fetchStates);
const { data: counties } = useQuery(
["counties", selectedState],
() => fetchCounties(selectedState),
{ enabled: !!selectedState }
);
const { data: towns } = useQuery(
["towns", selectedCounty],
() => fetchTowns(selectedCounty),
{ enabled: !!selectedCounty }
);
return (
<div className="App">
<h1>Location Selector</h1>
<select
value={selectedState}
onChange={(e) => {
setSelectedState(e.target.value);
setSelectedCounty(null);
}}
>
<option value="">Select State</option>
{states?.map((state) => (
<option key={state.id} value={state.id}>
{state.name}
</option>
))}
</select>
{selectedState && (
<select
value={selectedCounty}
onChange={(e) => setSelectedCounty(e.target.value)}
>
<option value="">Select County</option>
{counties?.map((county) => (
<option key={county.id} value={county.id}>
{county.name}
</option>
))}
</select>
)}
{selectedCounty && (
<select>
<option value="">Select Town</option>
{towns?.map((town) => (
<option key={town.id} value={town.id}>
{town.name}
</option>
))}
</select>
)}
</div>
);
}
export default App;
In the code above, we create the App component, import the necessary dependencies, and define three functions to fetch states, counties, and towns from the API. We then use React Query’s useQuery
hook to fetch the data and store it in the component’s state. Based on the user’s selection of state and county, we conditionally render the county and town dropdown menus.
Step 3: Configure Proxy for the Development Server
To avoid CORS issues while making API requests from the frontend, add a proxy to the development server. In myapp-frontend/package.json
, add the following line:
"proxy": "http://localhost:8000",
This will proxy requests made by the development server to the Django back-end running on port
Step 4: Run the Application
There is some additional code if you are running bot the Django Server and the React Server ( as below )
Make sure you have react-query installed
npm install react-query
Modify your index.js
Add an import at the beginning of your index.js
import { QueryClient } from 'react-query';
Add a new instance below the imports ( in the index.js file )
const queryClient = new QueryClient();
Wrap your ‘App’ in the QueryClient ( in the index.js file )
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>,
You index.js should now look like this:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { QueryClient, QueryClientProvider } from 'react-query';
const root = ReactDOM.createRoot(document.getElementById('root'));
const queryClient = new QueryClient();
root.render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>,
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
4.1. Start the Django development server:
In the myproject
directory, run the following command:
$ python manage.py runserver
This will start the Django development server on http://localhost:8000
.
4.2. Start the React development server:
In the myapp-frontend
directory, run the following command:
$ npm start
This will start the React development server, which will automatically open your default web browser and navigate to http://localhost:3000
.
Now you should see the form with the state, county, and town dropdown menus. As you select a state and county, the corresponding counties and towns will be fetched and populated in the respective dropdowns.
Conclusion:
In this tutorial, we have demonstrated how to create a form with state, county, and town dropdowns using React, React Query, and Django. We utilized Django as our backend to serve the API, React to build the user interface, and React Query to manage data fetching and state management. This combination of technologies allows for a seamless and performant user experience while managing complex data dependencies.