React Design Patterns
Hi everyone, here is not getting started guide for writing React. You should have some basic of React such as basic concept of React and practical React tutorial.
Overview
I'm following Airbnb style guide and using react in pattern books for references https://github.com/krasimir/react-in-patterns, https://github.com/vasanthk/react-bits
Designing React Web Application
Nowadays, we have many approaches to design the composition of react component.
Here is one of approaches to design
Preparation
- Use Airbnb style guide for consistency code style
- Use popular React boilerplate (
create-react-app
) for reducing complexity to manage build tools such as webpack, babel, etc.
Basic Design
In React documentation describes Thinking in React for explaining how to design react component and how they compose each others. However, when I 've getting started with React, I realize the component design thinking is most important thing for designing react application. In a React application, it will be at least one component, or can be divided into child component which should work with the parent one.
- Start with a few React component for reducing the complexity in state management.
- Break the code down to component when necessary, if it feel easier.
- The component can break into 2 parts: Container Component and Presentational Component, when necessary.
- Container Component
- Presentational Component
Advanced Design: Reusable Components
- Break the code down to component if you want to reuse the components.
- Use High-order Component when necessary.
Composing components
- Passing a child as a prop
- Passing state through the props
- Using top component for storing state
- Using simple state management library for small application
- Honorable mention libraries: pure-store, unstated and unistoreother approaches are shown at React State Museum which good place for reviewing react state management library.
- I'm interesting in mobx approaches for small application
- Using Redux , mobx for big application
Alternative, you can use MVC approach if you familiar, however, in my opinion using MVC on react.
Design Patterns and Techniques
Component Organization Templates & Patterns
Full Feature (Stateful Component)
class Person extends React.Component {
constructor (props) {
super(props);
this.state = { smiling: false };
this.handleClick = () => {
this.setState({smiling: !this.state.smiling});
};
}
componentWillMount () {
// add event listeners (Flux Store, WebSocket, document, etc.)
}
componentDidMount () {
// React.getDOMNode()
}
componentWillUnmount () {
// remove event listeners (Flux Store, WebSocket, document, etc.)
}
get smilingMessage () {
return (this.state.smiling) ? "is smiling" : "";
}
render () {
return (
<div onClick={this.handleClick}>
{this.props.name} {this.smilingMessage}
</div>
);
}
}
Person.defaultProps = {
name: 'Guest'
};
Person.propTypes = {
name: React.PropTypes.string
};
Ref: https://github.com/chantastic/react-patterns#component-organization
Stateless Component
function StatelessComponent(){
return (
<div>
This is Stateless Component.
</div>
)
};
Stateless Component with props
function StatelessComponent({ data }){
return (
<div>
{data}
</div>
)
};
Container Component
We can split our component into 2 types: Container component, Presentational component
// CommentList.js
class CommentList extends React.Component {
render() {
return (
<ul>
{this.props.comments.map(({body, author}) => {
return <li>{body}—{author}</li>;
})}
</ul>
);
}
}
// CommentListContainer.js
class CommentListContainer extends React.Component {
getInitialState () {
return { comments: [] }
}
componentDidMount () {
$.ajax({
url: "/my-comments.json",
dataType: 'json',
success: function(comments) {
this.setState({comments: comments});
}.bind(this)
});
}
render () {
return <CommentList comments={this.state.comments} />;
}
}
Read more for Presentational and container components, using ref https://github.com/chantastic/react-patterns#container-components
JSX Rendering
if
{isActive && <p>Message</p>}
if-else
{
isTrue ? (
<span>Rendered when `TRUE`</span>
) : (
<span>Rendered when `FALSE`</span>
);
}
Loop items for JSX
const todos = [
{id: 1, text: 'Test'},
{id: 1, text: 'Write a paper'}
]
function sampleComponent(){
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
)
};
Two way binding
class App extends React.Component {
state = {
textbox: ""
}
render() {
return (
<div>
<input
type="text"
value={this.state.textbox}
onChange={(e) => this.setState({ textbox: e.target.value })} />
{this.state.textbox}
</div>
);
}
}
Composing components
File Structure
Todo Components
├── components
│ ├── todo
│ ├── index.jsx
│ ├── AddTodo.jsx
│ └── TodoList
│ ├── index.jsx
│ └── Todo.jsx
├── app.js
└── index.js
Passing a child as a prop
Every React component has children
props.
const Title = function () {
return <h1>Hello there!</h1>;
}
const Header = function ({ title, children }) {
return (
<header>
{ title }
{ children }
</header>
);
}
function App() {
return (
<Header title={ <Title /> }>
Resting content
</Header>
);
};
ref: https://krasimir.gitbooks.io/react-in-patterns/content/chapter-04/#passing-a-child-as-a-prop
Passing state through the props
// Counter.jsx
class Counter extends React.Component {
state = {
count: 0
}
handleAdd(){
this.setState({ count: this.state.count + 1});
}
render() {
return (
<CounterView
value={this.state.count}
onAdd={() => this.handleAdd()}>
</CounterView>
);
}
}
// CounterView.jsx
function CounterView({ value, onAdd }) {
return (
<React.Fragment>
<div>Count: {value}</div>
<button onClick={onAdd}>Add</button>
</React.Fragment>
);
};
Using top component for storing state
// store.js
export default {
root: {},
getRoot() {
return this.root;
},
setRoot(value) {
this.root = value;
}
}
// Counter.jsx
class Counter extends React.Component {
constructor(props){
super(props);
this.state = {
count: 0
}
store.setRoot(this);
}
handleAdd(){
this.setState({ count: this.state.count + 1});
}
render() {
return (
<CounterView
value={this.state.count}></CounterView>
);
}
}
// CounterView.jsx
function CounterView({ value }) {
return (
<React.Fragment>
<div>Count: {value}</div>
<button onClick={() => store.getRoot().handleAdd()}>Add</button>
</React.Fragment>
);
};
Thanks for Anas
Share state between components
Design choice:
- Small Application: pure-store, unstated and unistore
- Small - Medium Application: mobx (Also using unstated and unistore )
- Using Redux , mobx for big application
Read more how to implement in each state management library in React State Museum
How to choose the composing component pattern?
Style Guide & Naming
Style Guide
Read more in Airbnb's Style guide
Naming Events
class Owner extends React.Component {
handleClick () {
// handle click event
}
render () {
return <div onClick={() => this.handleClick()}></div>;
}
}
Original: https://github.com/chantastic/react-patterns#naming-events
Other Resources
Other topics can read in below resources:
- React Design principles
- Airbnb React/JSX Style Guide
- React in pattern by krasimir
- React patterns at Planning Center Online
- React patterns by Michael Chan
- React patterns, techniques, tips and tricks
State
P.S. PR, Suggestions are welcome