Skip to content

Commit 7faadd8

Browse files
committed
Updated the example, improved documentation and cleaned some code.
1 parent 6a873bc commit 7faadd8

37 files changed

+19717
-183
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,5 @@ docs/_build/
5454
target/
5555

5656
node_modules
57-
tests/static_root
57+
tests/static_root
58+
example/static

README.md

Lines changed: 64 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,23 @@ Django React
33

44
[![Build Status](https://travis-ci.org/markfinger/django-react.svg?branch=master)](https://travis-ci.org/markfinger/django-react)
55

6-
Render React components from a Django application.
6+
Render and bundle React components from a Django application.
77

88
```python
99
from django_react.render import render_component
1010

11-
props = {
12-
'foo': 'bar',
11+
# Render a JSX component
12+
component = render_component('path/to/component.jsx', translate=True, props={
13+
'foo': 'bar',
1314
'woz': [1,2,3],
14-
}
15+
})
1516

16-
rendered = render_component('path/to/component.jsx', props=props)
17+
# The rendered markup
18+
print(component)
1719

18-
print(rendered)
20+
# Render JavaScript that will reuse the data provided and mount the component
21+
# on the client-side
22+
print(component.render_js())
1923
```
2024

2125
Documentation
@@ -88,37 +92,77 @@ Arguments:
8892
- `to_static_markup` *optional* — a boolean indicating that React's `renderToStaticMarkup`
8993
method should be used for the rendering. Defaults to `False`, which causes React's
9094
`renderToString` method to be used.
95+
- `bundle` *optional* - a boolean indicating that the component should be bundled for
96+
reuse on the client-side. If `translate` or `watch_source` are used, this argument is
97+
ignored.
98+
- `translate` *optional* - a boolean indicating that the component should be translated
99+
from JSX and ES6/7 before rendering.
91100
- `watch_source` *optional* — a boolean indicating that the renderer should watch your source
92-
files and rebuild the component everytime it changes. Defaults to `True`, in development.
93-
- `json_encoder` *optional* — a class which is used to encode the JSON which is sent to the
94-
renderer. Defaults to `django.core.serializers.json.DjangoJSONEncoder`.
101+
files and rebuild the component whenever it changes. If not defined, defaults to `DEBUG`.
102+
- `json_encoder` *optional* — a class which is used to encode the props to JSON. Defaults
103+
to `django.core.serializers.json.DjangoJSONEncoder`.
95104

96105

97106
RenderedComponent
98107
-----------------
99108

100-
The result of rendering a component to its initial HTML. RenderedComponents can be passed
101-
directly into templates where they output the generated HTML.
109+
The result of rendering a component to its initial markup. RenderedComponents can be passed
110+
directly into templates where they will output the generated markup.
102111

103112
```python
104113
# Render the component
105-
my_component = render_component(...)
114+
component = render_component(...)
106115

107-
# Print the generated HTML
108-
print(my_component)
116+
# Print the generated markup
117+
print(component)
109118
```
110119
```html
111120
<!-- Insert the generated HTML into your template -->
112-
{{ my_component }}
121+
{{ component }}
113122
```
114123

115-
RenderedComponents have a helper method, `render_props`, which outputs your JSON-serialized
116-
props. This allows you to reuse the encoded form of your props on the client-side.
124+
Components can be remounted on the client-side, so that the same codebase and data
125+
can be reused to provide interactivity.
117126

118127
```html
119-
<script>
120-
var myProps = {{ my_component.render_props }};
121-
</script>
128+
<script src="path/to/react.js"></script>
129+
130+
{{ component.render_js }}
131+
```
132+
133+
*Note*: if you wish to use the `render_js` method, you *must* provide a `<script>` element
134+
pointing to React. React is omitted from the bundled component so that build times are reduced,
135+
and to ensure that multiple components can be included on a single page without duplicating
136+
React's codebase.
137+
138+
Be aware that the mounting strategy used by `render_js` is fairly basic, if you want to use
139+
a more custom solution there are a couple of helpers provided to assist:
140+
```python
141+
The data used to render the component, this can be plugged straight into the client-side
142+
print(component.render_props())
143+
144+
# The bundled component (a WebpackBundle instance)
145+
component.get_bundle()
146+
147+
# Render a script element pointing to the bundled component
148+
print(component.get_bundle().render())
149+
150+
# The variable that the bundle exposes the component as on the global scope
151+
print(component.get_var())
152+
153+
# Returns an absolute path to the location of the component's bundle on the file-system
154+
print(component.bundle.get_path())
155+
156+
# When rendering a bundled component, the component is wrapped in a container
157+
# element to allow the mount JS to target it. You can use this selector to
158+
# target the container element
159+
print(component.get_container_id())
160+
161+
# The rendered markup without the container element wrapping it
162+
print(component.markup)
163+
164+
# Render the JS used to mount the bundled component over the rendered component
165+
print(component.render_mount_js())
122166
```
123167

124168

django_react/bundle.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66

77
COMPONENT_CONFIG_FILES = {}
88

9+
PATH_TO_NODE_MODULES = os.path.join(os.path.dirname(__file__), 'services', 'node_modules')
910

10-
def bundle_component(path, translate=None, var=None, watch=None):
11-
filename = get_component_config_filename(path, translate, var)
12-
return webpack(filename)
11+
12+
def bundle_component(path, translate=None, watch_source=None):
13+
filename = get_component_config_filename(path, translate)
14+
return webpack(filename, watch_source=watch_source)
1315

1416

1517
def get_var_from_path(path):
@@ -20,33 +22,32 @@ def get_var_from_path(path):
2022
return re.sub(r'\W+', '_', var)
2123

2224

23-
def get_webpack_config(path, translate=None, var=None):
24-
if var is None:
25-
var = get_var_from_path(path)
25+
def get_webpack_config(path, translate=None):
26+
var = get_var_from_path(path)
2627

2728
translate_config = ''
2829
if translate:
2930
# JSX + ES6/7 support
3031
translate_config += BUNDLE_TRANSLATE_CONFIG.format(
3132
ext=os.path.splitext(path)[-1],
32-
node_modules=os.path.join(os.path.dirname(__file__), 'services', 'node_modules')
33+
node_modules=PATH_TO_NODE_MODULES
3334
)
3435

3536
return BUNDLE_CONFIG.format(
36-
path_to_resolve=os.path.join(os.path.dirname(__file__), 'services', 'node_modules', 'resolve'),
37+
path_to_resolve=os.path.join(PATH_TO_NODE_MODULES, 'resolve'),
3738
dir=os.path.dirname(path),
3839
file='./' + os.path.basename(path),
3940
var=var,
4041
translate_config=translate_config
4142
)
4243

4344

44-
def get_component_config_filename(path, translate=None, var=None):
45-
cache_key = (path, translate, var)
45+
def get_component_config_filename(path, translate=None):
46+
cache_key = (path, translate)
4647
if cache_key in COMPONENT_CONFIG_FILES:
4748
return COMPONENT_CONFIG_FILES[cache_key]
4849

49-
config = get_webpack_config(path, translate, var)
50+
config = get_webpack_config(path, translate)
5051
filename = tempfile.mkstemp(suffix='.webpack.config.js')[1]
5152
with open(filename, 'w') as config_file:
5253
config_file.write(config)

django_react/render.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,6 @@ def get_var(self):
5959
def get_container_id(self):
6060
return 'reactComponent-' + self.get_var()
6161

62-
def get_props_var(self):
63-
return self.get_var() + '__props'
64-
6562
def render_mount_js(self):
6663
return mark_safe(
6764
MOUNT_JS.format(
@@ -102,7 +99,7 @@ def render_component(
10299

103100
bundled_component = None
104101
if bundle or translate or watch_source:
105-
bundled_component = bundle_component(path_to_source, translate=translate, watch=watch_source)
102+
bundled_component = bundle_component(path_to_source, translate=translate, watch_source=watch_source)
106103
path_to_source = bundled_component.get_assets()[0]['path']
107104

108105
if json_encoder is None:

example/README.md

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,43 @@
1-
Django + React + Webpack demo
2-
=============================
1+
django-react example
2+
====================
33

4-
This demo illustrates how:
4+
This example illustrates how:
55
- A single codebase can be used to generate server-side
6-
rendered HTML as well client-side interactivity.
6+
rendered HTML as well client-side interactivity.
77
- To pre-render HTML so that you can optimise for search-engines.
88
- Server-side rendering enables you to gracefully-degrade
9-
interactive features on clients without JavaScript enabled.
9+
interactive features on clients without JavaScript enabled.
1010

1111

12-
Install
13-
-------
12+
Run the example
13+
---------------
1414

1515
```bash
1616
# In the /example directory
1717

18-
mkvirtualenv django-react-demo
18+
# Create a virtual environment for the example
19+
mkvirtualenv django-react-example
20+
21+
# Install the project's python dependencies
1922
pip install -r requirements.txt
2023

21-
# Install the project's package dependencies
24+
# Install the project's JS dependencies
2225
./manage.py install_package_dependencies
2326

2427
# Start the node server that we use to render and bundle components
2528
./manage.py start_node_server
2629

27-
# In another terminal, start the django devserver
30+
# In another shell, start the django devserver
2831
./manage.py runserver
2932
```
3033

31-
And visit http://127.0.0.1:8000
34+
And visit [http://127.0.0.1:8000](http://127.0.0.1:8000)
3235

3336
**Note** that the first request may take a while to render, this is down to the
34-
node server having to read the app's codebase into memory and process dependencies.
35-
The initial overhead will only occurr on the first request, successive requests will
36-
be rendered immediately.
37+
node server having to read the app's codebase into memory. The initial overhead
38+
will only occur on the first request, subsequent requests will be rendered
39+
immediately.
3740

38-
If you make changes to the app's JS codebase, the node server will detect the changes
39-
and perform incremental rebuilds so that when the next request comes in, everything
40-
is ready and immediately responsive.
41+
If you make changes to the app's JS codebase, the node server will detect the
42+
changes and perform incremental rebuilds so that when the next request comes in,
43+
everything is ready and immediately responsive.

example/djangosite/settings.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@
2020
'django.contrib.staticfiles.finders.AppDirectoriesFinder'
2121
)
2222

23+
STATICFILES_DIRS = (
24+
os.path.join(BASE_DIR, 'djangosite', 'static'),
25+
)
26+
27+
TEMPLATE_DIRS = (
28+
os.path.join(BASE_DIR, 'djangosite', 'templates'),
29+
)
30+
2331
MIDDLEWARE_CLASSES = ()
2432

2533
ROOT_URLCONF = 'djangosite.urls'
@@ -41,6 +49,11 @@
4149
'PACKAGE_DEPENDENCIES': (),
4250
}
4351

52+
# Instruct django-node to install the package.json dependencies
53+
DJANGO_NODE['PACKAGE_DEPENDENCIES'] += (
54+
BASE_DIR,
55+
)
56+
4457

4558
# DJANGO WEBPACK
4659
# ==============
@@ -66,15 +79,4 @@
6679

6780
DJANGO_NODE['SERVICES'] += (
6881
'django_react.services',
69-
)
70-
71-
72-
# EXAMPLE APP
73-
# ===========
74-
75-
INSTALLED_APPS += (
76-
'example_app',
77-
)
78-
79-
# Instruct django-node to install the example app's package.json dependencies
80-
DJANGO_NODE['PACKAGE_DEPENDENCIES'] += (BASE_DIR,)
82+
)

0 commit comments

Comments
 (0)