book-react-in-patterns
book-react-in-patterns
1. Introduction 1.1
2. In brief 1.2
3.
4. Foundation
5. Communication 2.1
1. Input 2.1.1
2. Output 2.1.2
6. Event handlers 2.2
7. Composition 2.3
1. Using React's children
API 2.3.1
2. Passing a child as a prop
2.3.2
3. Higher-order component
2.3.3
4. Function as a children,
render prop 2.3.4
8. Controlled and uncontrolled
inputs 2.4
9. Presentational and container
components 2.5
10.
1
3.3.1
2. Simple counter app
using Redux 3.3.2
15.
22. Summary
2
Introduction
Introduction
React in patterns
A book about common design patterns used while
developing with React. It includes techniques for
composition, data flow, dependency management and
more.
Web (https://krasimir.gitbooks.io/react-in-
patterns/content/)
PDF (https://github.com/krasimir/react-in-
patterns/blob/master/book.pdf)
Mobi
(https://www.gitbook.com/download/mobi/book/k
rasimir/react-in-patterns)
ePub
(https://www.gitbook.com/download/epub/book/kr
asimir/react-in-patterns)
GitHub (https://github.com/krasimir/react-in-
patterns)
3
Introduction
4
In brief
In brief
In brief
This cookbook targets developers who
already have a basic understanding of
what React is and how it works. It's not
meant to be used as a complete how-to
guide, but as an introduction to popular
concepts and design patterns. Paradigms
that more or less have been introduced by
the community. It points you to thinking
in an abstract way. For example, instead
of talking about Flux, it talks about data
flow. Instead of talking about higher-
order components, it talks about
composition.
5
Communication
Communication
Communication
Every React component is like a small
system that operates on its own. It has its
own state, input and output. In the
following section we will explore these
characteristics.
Input-Output
Input
The input of a React component is its
props. That's how we pass data to it:
// Title.jsx
function Title(props) {
return <h1>{ props.text }
</h1>;
}
Title.propTypes = {
text: PropTypes.string
};
Title.defaultProps = {
text: 'Hello world'
};
// App.jsx
function App() {
return <Title text='Hello
React' />;
}
6
Communication
function SomethingElse({
answer }) {
return <div>The answer is
{ answer }</div>;
}
function Answer() {
return <span>42</span>;
}
7
Communication
}
function App() {
return (
<Title text='Hello
React'>
<span>community</span>
</Title>
);
}
In this example
<span>community</span> in App
component is children in Title
component. Notice that if we don't return
{ children } as part of the Title's
body the <span> tag will not be
rendered.
Output
The first obvious output of a React
component is the rendered HTML.
Visually that is what we get. However,
because the prop may be everything
including a function we could also send
out data or trigger a process.
function NameField({
valueUpdated }) {
return (
<input
8
Communication
9
Communication
this.props.results } />;
} else {
return <LoadingScreen
/>
}
}
}
Final thoughts
It is nice that we may think about every
React component as a black box. It has its
own input, lifecycle and output. It is up to
us to compose these boxes. And maybe
that is one of the advantages that React
offers. Easy to abstract and easy to
compose.
10
Event handlers
Event handlers
Event handlers
React provides a series of attributes for
handling events. The solution is almost
the same as the one used in the standard
DOM. There are some differences like
using camel case or the fact that we pass
a function but overall it is pretty similar.
const theLogoIsClicked = ()
=> alert('Clicked');
<Logo onClick={
theLogoIsClicked } />
<input
type='text'
onChange={event =>
theInputIsChanged(event.targ
et.value) } />
11
Event handlers
_handleButtonClick() {
console.log('Button is
clicked');
}
};
12
Event handlers
<button onClick={
this._handleButtonClick.bind
(this) }>
click me
</button>
13
Event handlers
this._onFieldChange.bind(thi
s, 'password');
}
render() {
return (
<form>
<input onChange={
this._onNameChanged } />
<input onChange={
this._onPasswordChanged } />
</form>
);
}
_onFieldChange(field,
event) {
console.log(`${ field }
changed to ${
event.target.value }`);
}
};
Final thoughts
There is not much to learn about event
handling in React. The authors of the
library did a good job in keeping what's
already there. Since we are using HTML-
14
Event handlers
15
Composition
Composition
Composition
One of the biggest benefits of React is
composability. I personally don't know a
framework that offers such an easy way
to create and combine components. In
this section we will explore a few
composition techniques which proved to
work well.
// app.jsx
import Header from
'./Header.jsx';
// Header.jsx
import Navigation from
'./Navigation.jsx';
16
Composition
// Navigation.jsx
export default function
Navigation() {
return (<nav> ... </nav>);
}
17
Composition
18
Composition
Higher-order component
For a long period of time higher-order
components were the most popular way
to enhance and compose React elements.
They look really similar to the decorator
design pattern because we have
component wrapping and enhancing.
19
Composition
var enhanceComponent =
(Component) =>
class Enhance extends
React.Component {
render() {
return (
<Component
{...this.props} />
)
}
};
20
Composition
var config =
require('path/to/configurati
on');
var enhanceComponent =
(Component) =>
class Enhance extends
React.Component {
render() {
return (
<Component
{...this.props}
title={
config.appTitle }
/>
)
}
};
var OriginalTitle = ({
title }) => <h1>{ title }
</h1>;
var EnhancedTitle =
enhanceComponent(OriginalTit
le);
21
Composition
var enhanceComponent =
(Component) =>
class Enhance extends
React.Component {
constructor(props) {
super(props);
this.state = {
remoteTitle: null };
}
componentDidMount() {
fetchRemoteData('path/to/end
point').then(data => {
this.setState({
remoteTitle: data.title });
});
}
render() {
return (
<Component
{...this.props}
title={
config.appTitle }
remoteTitle={
this.state.remoteTitle }
/>
)
}
};
var OriginalTitle = ({
title, remoteTitle }) =>
<h1>{ title }{ remoteTitle
}</h1>;
var EnhancedTitle =
enhanceComponent(OriginalTit
le);
22
Composition
Function as a children,
render prop
For the last couple of months, the React
community started shifting in an
interesting direction. So far in our
examples the children prop was a
React component. There is however a
new pattern gaining popularity in which
the same children prop is a JSX
expression. Let's start by passing a simple
object.
23
Composition
function App() {
const user = {
firstName: 'Krasimir',
lastName: 'Tsonev'
};
return (
<UserName>{ user }
</UserName>
);
}
24
Composition
);
}
function App() {
const todos = [
{ label: 'Write tests',
status: 'done' },
{ label: 'Sent report',
status: 'progress' },
{ label: 'Answer
emails', status: 'done' }
];
const isCompleted = todo
=> todo.status === 'done';
return (
<TodoList todos={ todos
}>
{
todo =>
isCompleted(todo) ?
<b>{ todo.label }
</b> :
todo.label
}
</TodoList>
);
}
25
Composition
return (
<TodoList
todos={ todos }
render={
todo =>
isCompleted(todo) ?
<b>{ todo.label }
</b> : todo.label
} />
);
this.state = { data:
null };
setTimeout(() =>
26
Composition
<Authorize
permissionsInclude={[
'read:products' ]}
render={ () =>
<ProductsList /> } />
27
Composition
Final thoughts
Did you wonder why HTML is still here.
It was created in the dawn of the internet
and we still use it. That is because it's
highly composable. React and its JSX
looks like HTML on steroids and as such
it comes with the same capabilities. So,
make sure that you master the
composition because that is one of the
biggest benefits of React.
28
Controlled and uncontrolled inputs
Controlled and
uncontrolled inputs
Controlled and
uncontrolled inputs
These two terms controlled and
uncontrolled are very often used in the
context of forms management. controlled
input is an input that gets its value from a
single source of truth. For example the
App component below has a single
<input> field which is controlled:
29
Controlled and uncontrolled inputs
30
Controlled and uncontrolled inputs
type='text' defaultValue={
this.state.value } />
}
};
31
Controlled and uncontrolled inputs
Final thoughts
controlled versus uncontrolled inputs is
very often underrated. However I believe
that it is a fundamental decision because
it dictates the data flow in the React
component. I personally think that
uncontrolled inputs are kind of an anti-
pattern and I'm trying to avoid them
when possible.
32
Presentational and container components
Presentational and
container
components
Presentational and
container
components
Every beginning is difficult. React is no
exception and as beginners we also have
lots of questions. Where I'm supposed to
put my data, how to communicate
changes or how to manage state? The
answers of these questions are very often
a matter of context and sometimes just
practice and experience with the library.
However, there is a pattern which is used
widely and helps organizing React based
applications - splitting the component
into presentation and container.
33
Presentational and container components
this._updateTime.bind(this);
}
render() {
const time =
this._formatTime(this.state.
time);
return (
<h1>
{ time.hours } : {
time.minutes } : {
time.seconds }
</h1>
);
}
componentDidMount() {
this._interval =
setInterval(this._update,
1000);
}
componentWillUnmount() {
clearInterval(this._interval
);
}
_formatTime(time) {
var [ hours, minutes,
seconds ] = [
time.getHours(),
time.getMinutes(),
time.getSeconds()
].map(num => num < 10 ?
'0' + num : num);
34
Presentational and container components
};
ReactDOM.render(<Clock time=
{ new Date() }/>, ...);
The problems
There are couple of things happening in
our component. It looks like it has too
many responsibilities.
35
Presentational and container components
// Clock/index.js
import Clock from
'./Clock.jsx'; // <-- that's
the presentational component
36
Presentational and container components
componentDidMount() {
this._interval =
setInterval(this._update,
1000);
}
componentWillUnmount() {
clearInterval(this._interval
);
}
_extract(time) {
return {
hours:
time.getHours(),
minutes:
time.getMinutes(),
seconds:
time.getSeconds()
};
}
_updateTime() {
this.setState({
time: new
Date(this.state.time.getTime
() + 1000)
});
}
};
Presentational component
37
Presentational and container components
// Clock/Clock.jsx
export default function
Clock(props) {
var [ hours, minutes,
seconds ] = [
props.hours,
props.minutes,
props.seconds
].map(num => num < 10 ?
'0' + num : num);
Benefits
Splitting the components in containers
and presentation increases the reusability
of the components. Our Clock
function/component may exist in
application that doesn't change the time
or it's not working with JavaScript's Date
object. That's because it is pretty dummy.
No details about the data are required.
38
Presentational and container components
Final thoughts
The concept of container and presentation
is not new at all but it fits really nicely
with React. It makes our applications
better structured, easy to manage and
scale.
39
One direction data flow
40
One direction data flow
</button>
);
}
};
var Store = {
_flag: false,
set: function(value) {
this._flag = value;
},
get: function() {
return this._flag;
}
};
this.props.onChange(this.sta
te.flag);
});
}
}
render() {
41
One direction data flow
return (
<button onClick={
this._onButtonClick }>
{ this.state.flag ?
'lights on' : 'lights off' }
</button>
);
}
};
function App() {
return <Switcher onChange=
{ Store.set.bind(Store) }
/>;
};
42
One direction data flow
Store.set.bind(Store) } />
var Store = {
_handlers: [],
_flag: '',
subscribe:
function(handler) {
this._handlers.push(handler)
;
},
set: function(value) {
this._flag = value;
this._handlers.forEach(handl
er => handler(value))
},
43
One direction data flow
get: function() {
return this._flag;
}
};
this.state = { value:
Store.get() };
Store.subscribe(value =>
this.setState({ value }));
}
render() {
return (
<div>
<Switcher
value={
this.state.value }
onChange={
Store.set.bind(Store) } />
</div>
);
}
};
44
One direction data flow
: 'lights off' }
</button>
);
};
<Switcher
value={ Store.get() }
onChange={
Store.set.bind(Store) } />
Final thoughts
The benefit that comes with this pattern is
that our components become dummy
representation of the store's data. There is
only one source of truth and this makes
the development easier. If you are going
to take one thing from this book, I would
prefer it to be this chapter. The one-
direction data flow drastically changed
how I think when designing a feature so I
believe it will have the same effect on
you.
45
Flux
Flux
Flux
I'm obsessed by making my code simpler.
I didn't say smaller because having less
code doesn't mean that is simple and easy
to work with. I believe that big part of the
problems in the software industry come
from the unnecessary complexity.
Complexity which is a result of our own
abstractions. You know, we (the
programmers) like to abstract. We like
placing things in black boxes and hope
that these boxes work together.
46
Flux
Implementing a Flux
architecture
As every other popular concept Flux also
has some variations. Very often to
understand something we have to
implement it. In the next few sections we
will create a library that provides helpers
for building the Flux pattern.
The dispatcher
47
Flux
the dispatcher
this._stores.forEach(functio
n (entry) {
entry.store.update(action);
});
}
}
}
};
48
Flux
}
}
Using a helper
Framework.attachToStore(view
, store);
With a mixin
49
Flux
var View =
React.createClass({
mixins:
[Framework.attachToStore(sto
re)]
...
});
Using a context
50
Flux
function
attachToStore(Component,
store, consumer) {
const Wrapper =
React.createClass({
getInitialState() {
return
consumer(this.props, store);
},
componentDidMount() {
store.onChangeEvent(this._ha
ndleStoreChange);
},
componentWillUnmount() {
store.offChangeEvent(this._h
andleStoreChange);
},
_handleStoreChange() {
if (this.isMounted())
{
this.setState(consumer(this.
props, store));
}
},
render() {
return <Component
51
Flux
{...this.props}
{...this.state} />;
}
});
return Wrapper;
};
ProfilePage =
connectToStores(MyView,
store, (props, store) => ({
data: store.get('key')
}));
My choice
52
Flux
53
Flux
consumers.push(consumer);
};
this._stores.push({
store: store });
return subscribe;
}
return false;
}
54
Flux
consumers.forEach(function
(consumer) {
consumer(store);
});
};
var subscribe = function
(consumer) {
consumers.push(consumer);
};
this._stores.push({
store: store, change: change
});
return subscribe;
}
return false;
},
dispatch: function (action)
{
if (this._stores.length >
0) {
this._stores.forEach(functio
n (entry) {
entry.store.update(action,
entry.change);
});
55
Flux
}
}
56
Flux
consumers.forEach(function
(consumer) {
consumer(store);
});
};
var subscribe =
function (consumer, noInit)
{
consumers.push(consumer);
!noInit ?
consumer(store) : null;
};
this._stores.push({
store: store, change: change
});
return subscribe;
}
return false;
},
dispatch: function
(action) {
if
(this._stores.length > 0) {
this._stores.forEach(functio
n (entry) {
entry.store.update(action,
entry.change);
});
}
}
}
};
The actions
57
Flux
{
type:
'USER_LOGIN_REQUEST',
payload: {
username: '...',
password: '...'
}
}
58
Flux
}
}
}
var createSubscriber =
function (store) {
return
dispatcher.register(store);
}
59
Flux
consumers.forEach(function
(consumer) {
consumer(store);
});
};
var subscribe =
function (consumer, noInit)
{
consumers.push(consumer);
!noInit ?
consumer(store) : null;
};
this._stores.push({
store: store, change: change
});
return subscribe;
}
60
Flux
return false;
},
dispatch: function
(action) {
if
(this._stores.length > 0) {
this._stores.forEach(functio
n (entry) {
entry.store.update(action,
entry.change);
});
}
}
}
};
module.exports = {
create: function () {
var dispatcher =
Dispatcher();
return {
createAction: function
(type) {
if (!type) {
throw new
Error('Please, provide
action\'s type.');
} else {
return function
(payload) {
return
dispatcher.dispatch({
type: type,
payload:
payload
});
}
}
},
createSubscriber:
61
Flux
function (store) {
return
dispatcher.register(store);
}
}
}
};
Wrapping up
We have a module that provides two
helpers for building a Flux project. Let's
write a simple counter app that doesn't
involve React so we see the pattern in
action.
The markup
<div id="counter">
<span></span>
<button>increase</button>
<button>decrease</button>
</div>
The view
62
Flux
document.querySelector('#cou
nter');
var display =
el.querySelector('span');
var [ increaseBtn,
decreaseBtn ] =
Array.from(el.querySelectorA
ll('button'));
subscribeToStore([updateStat
e, render]);
increaseBtn.addEventListener
('click', increase);
decreaseBtn.addEventListener
('click', decrease);
};
63
Flux
The store
const CounterStore = {
_data: { value: 0 },
getValue: function () {
return this._data.value;
},
update: function (action,
change) {
if (action.type ===
INCREASE) {
this._data.value += 1;
} else if (action.type
=== DECREASE) {
this._data.value -= 1;
}
change();
}
};
64
Flux
const { createAction,
createSubscriber } =
Fluxiny.create();
const counterStoreSubscriber
=
createSubscriber(CounterStor
e);
const actions = {
increase:
createAction(INCREASE),
decrease:
createAction(DECREASE)
};
View(counterStoreSubscriber,
actions.increase,
actions.decrease);
A live demo
65
Flux
66
Redux
Redux
Redux
Redux is a library that acts as a state
container and helps managing your
application data flow. It was introduced
back in 2015 at ReactEurope conference
(video) by Dan Abramov. It is similar to
Flux architecture and has a lot in
common with it. In this section we will
create a small counter app using Redux
alongside React.
67
Redux
Actions
const CHANGE_VISIBILITY =
'CHANGE_VISIBILITY';
const action = {
type: CHANGE_VISIBILITY,
visible: false
}
68
Redux
const changeVisibility =
visible => ({
type: CHANGE_VISIBILITY,
visible
});
changeVisibility(false);
// { type:
CHANGE_VISIBILITY, visible:
false }
Store
createStore([reducer],
[initial state],
[enhancer]);
69
Redux
store.dispatch(changeVisibil
ity(false));
Reducer
70
Redux
const counterReducer =
function (state, action) {
if (action.type === ADD) {
return { value:
state.value + 1 };
} else if (action.type ===
SUBTRACT) {
return { value:
state.value - 1 };
}
return { value: 0 };
};
71
Redux
connect(
[mapStateToProps],
[mapDispatchToProps],
[mergeProps],
[options]
)
mapStateToProps parameter is a
function that accepts the current state and
must return a set of key-value pairs (an
object) that are getting send as props to
our React component. For example:
const mapStateToProps =
state => ({
visible: state.visible
});
72
Redux
mapDispatchToProps is a similar
one but instead of the state receives a
dispatch function. Here is the place
where we can define a prop for
dispatching actions.
const mapDispatchToProps =
dispatch => ({
changeVisibility: value =>
dispatch(changeVisibility(va
lue))
});
73
Redux
const initialState = {
counter: {
value: 0
},
74
Redux
visible: true
};
import { createStore,
combineReducers } from
'redux';
const rootReducer =
combineReducers({
counter: function A() {
... },
visible: function B() {
... }
});
const store =
createStore(rootReducer);
const counterReducer =
function (state, action) {
75
Redux
const visibilityReducer =
function (state, action) {
if (action.type ===
CHANGE_VISIBILITY) {
return action.visible;
}
return true;
};
const rootReducer =
combineReducers({
counter: counterReducer,
visible: visibilityReducer
});
76
Redux
Selectors
const getCounterValue =
state =>
state.counter.value;
const getVisibility = state
=> state.visible;
React components
function Visibility({
changeVisibility }) {
return (
77
Redux
<div>
<button onClick={ ()
=> changeVisibility(true) }>
Visible
</button>
<button onClick={ ()
=> changeVisibility(false)
}>
Hidden
</button>
</div>
);
}
const VisibilityConnected =
connect(
null,
dispatch => ({
changeVisibility: value
=>
dispatch(changeVisibility(va
lue))
})
)(Visibility);
78
Redux
const CounterConnected =
connect(
state => ({
value:
getCounterValue(state)
}),
dispatch => ({
add: () =>
dispatch(add()),
subtract: () =>
dispatch(subtract())
})
)(Counter);
79
Redux
{ visible &&
<CounterConnected /> }
</div>
);
}
const AppConnected =
connect(
state => ({
visible:
getVisibility(state)
})
)(App);
Final thoughts
Redux is a wonderful pattern. Over the
years the JavaScript community
developed the idea and enhanced it with
couple of new terms. I think a typical
redux application looks more like this:
Redux architecture
80
Dependency injection
Dependency
injection
Dependency
injection
Many of the modules/components that we
write have dependencies. A proper
management of these dependencies is
critical for the success of the project.
There is a technique (most people
consider it a pattern) called dependency
injection that helps solving the problem.
// Title.jsx
export default function
Title(props) {
return <h1>{ props.title }
</h1>;
}
// Header.jsx
import Title from
'./Title.jsx';
81
Dependency injection
// App.jsx
import Header from
'./Header.jsx';
// inject.jsx
const title = 'React in
patterns';
82
Dependency injection
<Component
{...this.props}
title={ title }
/>
)
}
};
}
// -------------------------
----------
// Header.jsx
import inject from
'./inject.jsx';
import Title from
'./Title.jsx';
var EnhancedTitle =
inject(Title);
export default function
Header() {
return (
<header>
<EnhancedTitle />
</header>
);
}
83
Dependency injection
84
Dependency injection
childContextTypes and
contextTypes. If those are not
specified then the context object will
be empty. That can be a little bit
frustrating because we may have lots of
stuff to put there. That is why it is a good
practice that our context is not just a
plain object but it has an interface that
allows us to store and retrieve data. For
example:
// dependencies.js
export default {
data: {},
get(key) {
return this.data[key];
},
register(key, value) {
this.data[key] = value;
}
}
dependencies.register('title
', 'React in patterns');
85
Dependency injection
get: React.PropTypes.func,
register:
React.PropTypes.func
};
// Title.jsx
export default class Title
extends React.Component {
render() {
return <h1>{
this.context.get('title') }
</h1>
}
}
Title.contextTypes = {
data:
React.PropTypes.object,
get: React.PropTypes.func,
register:
React.PropTypes.func
};
// Title.jsx
import wire from './wire';
function Title(props) {
86
Dependency injection
this.context.get.bind(this.c
ontext)
);
var props =
mapper(...resolved);
return
87
Dependency injection
React.createElement(Componen
t, props);
}
}
Inject.contextTypes = {
data:
React.PropTypes.object,
get:
React.PropTypes.func,
register:
React.PropTypes.func
};
return Inject;
};
88
Dependency injection
// context.js
import { createContext }
from 'react';
const Context =
createContext({});
89
Dependency injection
}
};
function Title() {
return (
<Consumer>{
({ title }) =>
<h1>Title: { title }</h1>
}</Consumer>
);
}
90
Dependency injection
export function
register(key, dependency) {
dependencies[key] =
dependency;
}
91
Dependency injection
export function
wire(Component, deps,
mapper) {
return class Injector
extends React.Component {
constructor(props) {
super(props);
this._resolvedDependencies =
mapper(...deps.map(fetch));
}
render() {
return (
<Component
{...this.state}
{...this.props}
{...this._resolvedDependenci
es}
/>
);
}
};
}
92
Dependency injection
// app.jsx
import Header from
'./Header.jsx';
import { register } from
'./di.jsx';
register('my-awesome-title',
'React in patterns');
// -------------------------
----------
// Header.jsx
import Title from
'./Title.jsx';
// -------------------------
----------
// Title.jsx
import { wire } from
'./di.jsx';
93
Dependency injection
Final thoughts
Dependency injection is a tough problem.
Especially in JavaScript. Lots of people
didn't realize that but putting a proper
dependency management is a key process
of every development cycle. JavaScript
ecosystem offers different tools and we as
developers should pick the one that fits in
our needs.
94
Styling
Styling
Styling React
components
React is a view layer. As such it kind of
controls the markup rendered in the
browser. And we know that the styling
with CSS is tightly connected to the
markup on the page. There are couple of
approaches for styling React applications
and in this section we will go through the
most popular ones.
<h1
className='title'>Styling</h
1>
Inline styling
The inline styling works just fine.
Similarly to HTML we are free to pass
styles directly via a style attribute.
However, while in HTML the value is a
string, in JSX it must be an object.
95
Styling
const inlineStyles = {
color: 'red',
fontSize: '10px',
marginTop: '2em',
'border-top': 'solid 1px
#000'
};
const theme = {
fontFamily: 'Georgia',
color: 'blue'
};
const paragraphText = {
...theme,
fontSize: '20px'
};
CSS modules
96
Styling
/* style.css */
.title {
color: green;
}
// App.jsx
import styles from
"./style.css";
function App() {
return <h1 style={
styles.title }>Hello
world</h1>;
}
.title {
composes: mainColor from
97
Styling
"./brand-colors.css";
}
Styled-components
Styled-components took another
direction. Instead of inlining styles the
library provides a React component. We
then use this component to represent a
specific look and feel. For example, we
may create a Link component that has
certain styling and use that instead of the
<a> tag.
<Link
href='http://google.com'>Goo
gle</Link>
const AnotherLink =
styled(Link)`
color: blue;
`;
<AnotherLink
href='http://facebook.com'>F
acebook</AnotherLink>
98
Styling
Final thoughts
There are multiple ways to style your
React application. I experienced all of
them in production and I would say that
there is no right or wrong. As most of the
stuff in JavaScript today you have to pick
the one that fits better in your context.
99
Integration of third-party libraries
Integration of
third-party
libraries
Third-party
integration
React is probably one of the best choices
for building UI. Good design, support
and community. However, there are cases
where we want to use an external service
or we want to integrate something
completely different. We all know that
React works heavily with the actual
DOM and basically controls what's
rendered on the screen. That's why
integrating of third-party components
may be tricky. In this section we will see
how to mix React and jQuery's UI plugin
and do it safely.
The example
I picked tag-it jQuery plugin for my
example. It transforms an unordered list
to input field for managing tags:
<ul>
<li>JavaScript</li>
<li>CSS</li>
</ul>
to:
tag-it
100
Integration of third-party libraries
$('<dom element
selector>').tagit();
// Tags.jsx
class Tags extends
React.Component {
componentDidMount() {
// initialize tagit
$(this.refs.list).tagit();
}
render() {
return (
<ul ref="list">
{
this.props.tags.map(
(tag, i) => <li
key={ i }>{ tag } </li>
)
}
</ul>
);
}
};
// App.jsx
class App extends
React.Component {
constructor(props) {
super(props);
this.state = { tags:
['JavaScript', 'CSS' ] };
101
Integration of third-party libraries
}
render() {
return (
<div>
<Tags tags={
this.state.tags } />
</div>
);
}
}
ReactDOM.render(<App />,
document.querySelector('#con
tainer'));
Force a single-render
The very first thing that we have to do is
to force a single-render of the Tags
component. That is because when React
adds the elements in the actual DOM we
want to pass the control of them to
jQuery. If we skip this both React and
jQuery will work on same DOM
elements without knowing for each other.
To achieve a single-render we have to use
the lifecycle method
shouldComponentUpdate like so:
102
Integration of third-party libraries
}
...
103
Integration of third-party libraries
this._addNewTag =
this._addNewTag.bind(this);
this.state = {
104
Integration of third-party libraries
tags: ['JavaScript',
'CSS' ],
newTag: null
};
}
_addNewTag() {
this.setState({ newTag:
this.refs.field.value });
}
render() {
return (
<div>
<p>Add new tag:</p>
<div>
<input type='text'
ref='field' />
<button onClick={
this._addNewTag
}>Add</button>
</div>
<Tags
tags={
this.state.tags }
newTag={
this.state.newTag } />
</div>
);
}
}
105
Integration of third-party libraries
componentWillReceiveProps(ne
wProps) {
this.list.tagit('createTag',
newProps.newTag);
}
...
.tagit('createTag',
newProps.newTag) is a pure jQuery
code.
componentWillReceiveProps is a
nice place for calling methods of the
third-party library.
componentWillReceiveProps(ne
wProps) {
this.list.tagit('createTag',
newProps.newTag);
}
106
Integration of third-party libraries
render() {
return (
<ul ref='list'>
{
this.props.tags.map(
(tag, i) => <li
key={ i }>{ tag } </li>
)
}
</ul>
);
}
};
Final thoughts
Even though React is manipulating the
DOM tree we are able to integrate third-
party libraries and services. The available
lifecycle methods give us enough control
on the rendering process so they are the
perfect bridge between React and non-
React code.
107
React and separation of concerns
React and
separation of
concerns
React and
separation of
concerns
Years ago when Facebook announced
their JSX syntax we had a wave of
comments how this was against some of
the well established good practices. The
main point of most people was that it
violates the separation of concerns. They
said that React and its JSX are mixing
HTML, CSS and JavaScript which were
suppose to be separated.
Styling
React components render to DOM
elements. Nothing stops us to use the
good old class attribute and attach a
CSS class to the produced HTML
element. The only one difference is that
the attribute is called className
instead of class. The rest still works
which means that if we want we may put
108
React and separation of concerns
// assets/css/styles.css
.pageTitle {
color: pink;
}
// assets/js/app.js
function PageTitle({ text })
{
return <h1
className='pageTitle'>{ text
}</h1>;
}
109
React and separation of concerns
{avatarStyles} />
<p>{name}</p>
</div>
);
}
110
React and separation of concerns
<Avatar url={avatar}
/>
<p>{name}</p>
</Card>
);
}
Logic
Very often we write logic inside our
React components which is more then
clicking a button and showing a message.
The snippet below demonstrates a
component that fetches data from a fake
API and renders users on the screen.
this.state = {
loading: false,
users: null,
error: null
111
React and separation of concerns
};
}
componentDidMount() {
this.setState({ loading:
true }, () => {
fetch('https://jsonplacehold
er.typicode.com/users')
.then(response =>
response.json())
.then(users =>
this.setState({ users,
loading: false }))
.catch(error =>
this.setState({ error,
loading: false }));
});
}
render() {
const { loading, users,
error } = this.state;
if (isRequestInProgress)
return <p>Loading</p>;
if (error) return
<p>Ops, sorry. No data
loaded.</p>;
if (users) return
users.map(({ name }) => <p>
{name}</p>);
return null;
}
}
112
React and separation of concerns
this.state = {
loading: false,
users: null,
error: null
};
}
componentDidMount() {
this.setState({ loading:
true }, () => {
fetch('https://jsonplacehold
er.typicode.com/users')
.then(response =>
response.json())
.then(users =>
this.setState({ users,
loading: false }))
.catch(error =>
this.setState({ error,
loading: false }));
});
}
render() {
113
React and separation of concerns
return
this.props.children({
loading, users, error });
}
}
function App() {
return (
<FetchUsers>
{({ loading, users,
error }) => {
if (loading) return
<p>Loading</p>;
if (error) return
<p>Ops, sorry. No data
loaded.</p>;
if (users) return
users.map(({ name }) => <p>
{name}</p>);
return null;
}}
</FetchUsers>
);
}
114
React and separation of concerns
Markup
JSX syntax is following the XML/HTML
semantics and as such comes with a huge
benefit - composability. My opinion is
that React is one level up over the HTML
because it allows us to group complex
markup into a single component. For
example we have a <header> with
some <h1>, <nav> and <p> tags inside.
We may easily create a <Header>
component and put all those bits inside.
We still keep them together but now they
are easy to move around. Perhaps, there
are places where we write some logic
directly into the markup like so:
function
CallToActionButton({
service, token }) {
return <button onClick={
() => service.request(token)
} />;
}
<CallToAction server={
service } token={ token } />
function
CallToActionButton({
onButtonClicked }) {
return <button onClick={
onButtonClicked } />;
}
115
React and separation of concerns
Conclusion
No, I don't think that React is against the
separation of concerns. It is all about
design and composition. There are
patterns that help us to compose and
logically separate our apps. We can still
write well organized programs with
clearly defined responsibilities.
116
Summary
Summary
Summary
React became one of the most popular
libraries for building UIs. It comes with a
great API which is simple and powerful.
The tricky part though is that React itself
is not always enough for building
complex applications. There are concepts
that we must know to make it right.
Design patterns that are introduced by the
community and work well at scale. This
book teaches most of those patterns in a
slightly opinionated style. I hope you
liked it :)
117