SlideShare a Scribd company logo
React + Redux
Best practices
Борис Стринжа
Front-End Developer в Clickky
1. Create-react-app tool
1. Smart and Dumb components
2. Stateful and Stateless components
3. Binding
4. ShouldComponentUpdate
5. propTypes
6. Ducks pattern
7. Memoization (reselect)
8. Middlewares
React + Redux. Best practices
План
Почему мы используем
React?
React + Redux. Best practices
React + Redux. Best practices
Create React apps with no build configuration.
npm install -g create-react-app
create-react-app my-app
cd my-app/
npm start
React + Redux. Best practices
React + Redux. Best practices
To get started, edit src/App.js and save to reload
React + Redux. Best practices
package.json
{
"name": "my-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^15.4.2",
"react-dom": "^15.4.2"
},
"devDependencies": {
"react-scripts": "0.9.5"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
React + Redux. Best practices
npm run eject
Note: this is a one-way operation. Once you eject,
you can’t go back!
React + Redux. Best practices
Smart and Dumb
components
пример Dumb component
React + Redux. Best practices
пример Smart component
React + Redux. Best practices
Dumb
import React from 'react';
export default class Dumb extends React.Component {
selectNotificationItem = () => {
const {id} = this.props;
this.props.selectItem(id);
}
render () {
const {text, time} = this.props;
return (
<div className="item">
<span className="item__time">{time}</span>
<p className="item__text" onClick={this.selectNotificationItem}>{text}</p>
</div>
);
}
}
React + Redux. Best practices
<div className="list">
{list.map(item => {
return (
<Dumb
key={item.id}
id={item.id}
time={item.time}
text={item.text}
selectItem={this.handleSelectItem}
/>
);
})}
</div>
List.js
React + Redux. Best practices
Smartimport React from 'react';
import {connect} from 'react-redux';
import './App.css';
import Dumb from 'components/Dumb';
import {getNotificationsList, selectItem} from 'actions/notificatios';
class App extends React.Component {
componentWillMount() {
this.props.getNotificationsList();
}
handleSelectItem = (id) => {
this.props.selectItem(id);
}
render() {
const {list} = this.props;
return (
<div className="list">
{list.map(item => {
return (
<Dumb key={item.id} id={item.id} time={item.time} text={item.text} selectItem={this.handleSelectItem} />
);
})}
</div>
);
}
}
const mapStateToProps = (state) => ({
list:state.notifications.list
});
export default connect(mapStateToProps, {
getNotificationsList,
selectItem
})(App);
React + Redux. Best practices
Глупые компоненты:
1. Не зависят от остальной части приложения, например от redux actions или stores
2. Получают данные и колбэки исключительно через props
3. Имеют свой css файл
4. Изредка имеют свой state
5. Могут использовать другие глупые компоненты
Умные компоненты:
1. Оборачивает один или несколько глупых или умных компонентов
2. Хранит состояние стора и пробрасывает его как объекты в глупые компоненты
3. Вызывает redux actions и обеспечивает ими глупые компоненты в виде колбэков
4. Никогда не имеют собственных стилей
5. Редко сами выдают DOM, используйте глупые компоненты для макета
React + Redux. Best practices
Stateful and Stateless
components
React + Redux. Best practices
Stateful
import React from 'react';
export default class Counter extends React.Component {
state = {
count: 1
};
componentWillMount() {
this.setState({
count: this.state.count + 1
});
}
render () {
const {count} = this.state;
const {text} = this.props;
return (
<div className="item">
<span className="item__count">{count}</span>
<p className="item__text">{text}</p>
</div>
);
}
}
React + Redux. Best practices
Stateless
import React from 'react';
const Counter = ({addCount, text, count}) => {
return (
<div className="item">
<span className="item__count" onClick={addCount}>
{count}
</span>
<p className="item__text">{text}</p>
</div>
);
}
export default Counter;
React + Redux. Best practices
Binding
React + Redux. Best practices
Bind in Render
handleSelectItem(id) {
this.props.selectItem(id);
}
render() {
const {list} = this.props;
return (
<div className="list">
{list.map(item => {
return (
<div className="list__item" onClick={this.handleSelectItem.bind(this, item.id)}>
{item.text}
</div>
);
})}
</div>
);
}
handleSelectItem(id) {
this.props.selectItem(id);
}
render() {
const {list} = this.props;
return (
<div className="list">
{list.map(item => {
return (
<div className="list__item" onClick={(item) => this.handleSelectItem(item.id)}>
{item.text}
</div>
);
})}
</div>
);
}
React + Redux. Best practices
Arrow Function in Render
Bind in Constructor
constructor(props) {
super(props);
this.handleSelectItem = this.handleSelectItem.bind(this);
}
handleSelectItem(id) {
this.props.selectItem(id);
}
render() {
const {list} = this.props;
return (
<div className="list">
{list.map(item => {
return (
<div key={item.id} className="list__item">
<Dumb
id={item.id}
text={item.text}
selectItem={this.handleSelectItem}
/>
</div>
);
})}
</div>
);
}
React + Redux. Best practices
Arrow Function in Class Property
handleSelectItem = (id) => {
this.props.selectItem(id);
}
render() {
const {list} = this.props;
return (
<div className="list">
{list.map(item => {
return (
<div key={item.id} className="list__item">
<Dumb
id={item.id}
text={item.text}
selectItem={this.handleSelectItem}
/>
</div>
);
})}
</div>
);
}
React + Redux. Best practices
shouldComponentUpdate
React + Redux. Best practices
shouldComponentUpdate(nextProps, nextState) {
if (this.props.value !== nextProps.value) {
return true;
}
if (this.state.count !== nextState.count) {
return true;
}
return false;
}
component
React + Redux. Best practices
propTypes
export default class Activation extends React.Component {
static propTypes = {
query: React.PropTypes.object,
activatePublisher: React.PropTypes.func,
activateAdvertiser: React.PropTypes.func,
setActivationStatus: React.PropTypes.func,
isFetching: React.PropTypes.bool.isRequired,
activationStatus: React.PropTypes.bool.isRequired,
language: React.PropTypes.string
};
render() {
const {query} = this.props;
return(
<div></div>
);
}
}
components/Activation.js
React + Redux. Best practices
Console
React + Redux. Best practices
Ducks pattern
import {
NOTIFICATIONS_GET_LIST_REQUEST,
NOTIFICATIONS_GET_LIST_SUCCESS,
NOTIFICATIONS_GET_LIST_FAILURE,
NOTIFICATIONS_SELECT_ITEM
} from 'constants';
export function getNotificationsList() {
return {
types: [NOTIFICATIONS_GET_LIST_REQUEST, NOTIFICATIONS_GET_LIST_SUCCESS, NOTIFICATIONS_GET_LIST_FAILURE],
url: '/api/v1.0/notifications'
}
}
export function selectItem(data) {
return {
type: NOTIFICATIONS_SELECT_ITEM,
data
}
}
actions/notifications.js
React + Redux. Best practices
import {
NOTIFICATIONS_GET_LIST_REQUEST,
NOTIFICATIONS_GET_LIST_SUCCESS,
NOTIFICATIONS_GET_LIST_FAILURE,
NOTIFICATIONS_SELECT_ITEM
} from 'constants';
const initialState = {
list: [],
loading: false,
selected: null
};
export default (state = initialState, action = {}) => {
switch (action.type) {
case NOTIFICATIONS_GET_LIST_REQUEST:
return {
...state,
loading: true
};
case NOTIFICATIONS_GET_LIST_SUCCESS:
return {
...state,
list: actions.result,
loading: false
};
case NOTIFICATIONS_GET_LIST_FAILURE:
return {
...state,
loading: false
};
case NOTIFICATIONS_SELECT_ITEM:
return {
...state,
selected: action.data
};
default:
return state;
}
};
reducers/notifications.js
React + Redux. Best practices
export const NOTIFICATIONS_GET_LIST_REQUEST = 'NOTIFICATIONS_GET_LIST_REQUEST';
export const NOTIFICATIONS_GET_LIST_SUCCESS = 'NOTIFICATIONS_GET_LIST_SUCCESS';
export const NOTIFICATIONS_GET_LIST_FAILURE = 'NOTIFICATIONS_GET_LIST_FAILURE';
export const NOTIFICATIONS_SELECT_ITEM = 'NOTIFICATIONS_SELECT_ITEM';
constants/index.js
React + Redux. Best practices
// Actions
const GET_LIST_REQUEST = 'my-app/notifications/GET_LIST_REQUEST';
const GET_LIST_SUCCESS = 'my-app/notifications/GET_LIST_SUCCESS';
const GET_LIST_FAILURE = 'my-app/notifications/GET_LIST_FAILURE';
const SELECT_ITEM = 'my-app/notifications/SELECT_ITEM';
// Reducer
const initialState = {
list: [],
loading: false,
selected: null
};
export default function reducer(state = initialState, action = {}) => {
switch (action.type) {
case NOTIFICATIONS_GET_LIST_REQUEST:
return {
...state,
loading: true
};
case NOTIFICATIONS_GET_LIST_SUCCESS:
return {
...state,
list: actions.result,
loading: false
};
React + Redux. Best practices
modules/notifications.js
case NOTIFICATIONS_GET_LIST_FAILURE:
return {
...state,
loading: false
};
case NOTIFICATIONS_SELECT_ITEM:
return {
...state,
selected: action.data
};
default:
return state;
}
};
// Actions Creators
export function getNotificationsList() {
return {
types: [GET_LIST_REQUEST, GET_LIST_SUCCESS, GET_LIST_FAILURE],
url: '/api/v1.0/notifications'
}
}
export function selectItem(data) {
return {
type: SELECT_ITEM,
data
}
}
React + Redux. Best practices
1. MUST export default a function called reducer()
2. MUST export its action creators as functions
3. MUST have action types in the form
npm-module-or-app/reducer/ACTION_TYPE
4. MAY export its action types as UPPER_CASE, if an
external reducer needs to listen for them, or if it is a
published reusable library
React + Redux. Best practices
Requirements
Memoization
Memoization is an optimization technique used
primarily to speed up computer programs by storing
the results of expensive function calls and returning
the cached result when the same inputs occur again
React + Redux. Best practices
Reselect
import { createSelector } from 'reselect';
import {beautifyDate} from 'utils/date';
const data = (state) => state.notifications.data;
export const notificationsSelector = createSelector(
[data],
(notifications) => {
return notifications.reduce((list, item) => {
const title = beautifyDate(item.date, 'DD.MM.YYYY');
if (!list[title]) {
list[title] = [{...item}];
} else {
list[title] = [...list[title], {...item}];
}
return list;
}, {});
}
);
selectors/notifications.js
React + Redux. Best practices
import {notificationsSelector} from 'selectors/notifications';
const mapStateToProps = (state) => ({
list: notificationsSelector(state)
});
export default connect(mapStateToProps, {})(List);
containers/Notifications/index.js
React + Redux. Best practices
React + Redux. Best practices
Basic middleware
const customMiddleware = store => next => action => {
if (action.type !== 'custom') {
return next(action);
}
}
export default customMiddleware;
React + Redux. Best practices
store/index.js
import { createStore, applyMiddleware, } from 'redux'
import reducer from './reducer'
import customMiddleware from './customMiddleware'
const store = createStore(
reducer,
applyMiddleware(customMiddleware)
);
React + Redux. Best practices
middleware/apiMiddleware.js
import { push } from 'react-router-redux';
const apiMiddleware = (store) => (next) => (action) => {
const { url, types, method, body, ...rest } = action;
const { dispatch } = store;
if (!types) {
return next(action);
}
const [REQUEST, SUCCESS, FAILURE] = types;
next({...rest, type: REQUEST});
return fetch(url, {
method: method || 'GET',
credentials: 'same-origin',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
}).then((res) => {
if (res.status >= 200 && res.status < 300) {
return res.json();
} else {
next({...rest, error: res.statusText, type: FAILURE});
if (res.status === 401) {
window.localStorage.removeItem('user');
dispatch(push('/login'));
}
return Promise.reject(new Error(res.statusText));
}
}).then(({result, ...rest}) => {
next({...rest, result, type: SUCCESS});
});
};
export default apiMiddleware;
export function getNotificationsList(data) {
return {
types: [NOTIFICATIONS_GET_LIST_REQUEST, NOTIFICATIONS_GET_LIST_SUCCESS,
NOTIFICATIONS_GET_LIST_FAILURE],
url: '/api/v1.0/notifications',
method: POST
body: data
}
}
actions/notifications.js
React + Redux. Best practices
Делаем небольшие dumb компоненты, не боимся использовать
connect, в пользу читаемости кода
Для простого рендера используем stateless компоненты
Всегда используем propTypes
Контролируем перерендер с помощью shouldComponentUpdate
Используем мемоизацию с помощью reselect
Используем middleware
Выводы
React + Redux. Best practices
Вопросы
49

More Related Content

PPTX
PDF
Understanding React hooks | Walkingtree Technologies
PPTX
React workshop
PDF
React Js Simplified
PDF
React JS - Introduction
PDF
An introduction to React.js
PPTX
Intro to React
PPTX
Introduction to React JS for beginners | Namespace IT
Understanding React hooks | Walkingtree Technologies
React workshop
React Js Simplified
React JS - Introduction
An introduction to React.js
Intro to React
Introduction to React JS for beginners | Namespace IT

What's hot (20)

PPTX
React js programming concept
PDF
Introduction to React JS
PPTX
PDF
The virtual DOM and how react uses it internally
PPTX
What Is Virtual DOM In React JS.pptx
PPTX
PPTX
Introduction to React JS for beginners
PDF
An Introduction to ReactJS
PPTX
React + Redux Introduction
PDF
ReactJS presentation
PDF
PPTX
React js
PPTX
React js - The Core Concepts
PDF
react redux.pdf
PDF
Why and How to Use Virtual DOM
PPTX
React hooks
PDF
React
PDF
Understanding react hooks
PDF
React js t2 - jsx
PPTX
reactJS
React js programming concept
Introduction to React JS
The virtual DOM and how react uses it internally
What Is Virtual DOM In React JS.pptx
Introduction to React JS for beginners
An Introduction to ReactJS
React + Redux Introduction
ReactJS presentation
React js
React js - The Core Concepts
react redux.pdf
Why and How to Use Virtual DOM
React hooks
React
Understanding react hooks
React js t2 - jsx
reactJS
Ad

Similar to React + Redux. Best practices (20)

PDF
React lecture
PDF
Integrating React.js with PHP projects
PDF
Intro to React | DreamLab Academy
PDF
React redux
PDF
React, Redux and es6/7
PDF
Materi Modern React Redux Power Point.pdf
PDF
Intro to Redux | DreamLab Academy #3
PPTX
Battle of React State Managers in frontend applications
PDF
Reactивная тяга
PDF
Workshop 20: ReactJS Part II Flux Pattern & Redux
PDF
Using React, Redux and Saga with Lottoland APIs
PDF
Introducing Vuex in your project
PDF
State manager in Vue.js, from zero to Vuex
PDF
EWD 3 Training Course Part 39: Building a React.js application with QEWD, Part 3
PDF
MeetJS Summit 2016: React.js enlightenment
PDF
Recompacting your react application
PDF
Ngrx slides
PDF
Reactive.architecture.with.Angular
PDF
Server side rendering with React and Symfony
PDF
Introduction to Redux
React lecture
Integrating React.js with PHP projects
Intro to React | DreamLab Academy
React redux
React, Redux and es6/7
Materi Modern React Redux Power Point.pdf
Intro to Redux | DreamLab Academy #3
Battle of React State Managers in frontend applications
Reactивная тяга
Workshop 20: ReactJS Part II Flux Pattern & Redux
Using React, Redux and Saga with Lottoland APIs
Introducing Vuex in your project
State manager in Vue.js, from zero to Vuex
EWD 3 Training Course Part 39: Building a React.js application with QEWD, Part 3
MeetJS Summit 2016: React.js enlightenment
Recompacting your react application
Ngrx slides
Reactive.architecture.with.Angular
Server side rendering with React and Symfony
Introduction to Redux
Ad

More from Clickky (20)

PDF
Case-study: How to market ICO with success.
PDF
Clickhouse
PDF
Лучшие кейсы и практики монетизации мобильного трафика
PDF
Case study. Advertisers success in mobile economy
PDF
#MBC2016 Dan Ekstein, Supersonic Finding the Perfect Balance Between Rewarded...
PDF
#MBC2016 Кирилл Софронов, Remerge: Ретаргетинг в мобильных приложениях: опыт ...
PDF
#MBC2016 Илья Лагутин, Aitarget: Как эффективно продвигать мобильные приложен...
PDF
#MBC2016 Mark Ten, Sports.ru: Programmatic monetization of mobile apps
PDF
#MBC2016 Dvir Doron, Cedato: Mobile video - the new growth engine
PDF
#MBC2016 Bashir Modanov, NewBornTown: Globalized mobile ecosystem
PPTX
Как настроить B2B продажи за границей из Украины
PPTX
Как выжать еще больше из мобильного трафика?
PDF
AdExchange mediakit
PDF
Поймай меня, если сможешь
PPTX
WannaBiz Fund
PDF
AdExchange mediakit
PPTX
Clickky fraud presentation at Kinza 2015
PPTX
МАРКЕТИНГОВЫЕ ЦЕЛИ И ЗАКУПКА ТРАФИКА
PPTX
The trends of mobile in e-commerce, Mobilimes Serg Korneev
PDF
Инвестиции в mobile marketing, Алексей Тельнов iTech Capital, Mobile Beach Co...
Case-study: How to market ICO with success.
Clickhouse
Лучшие кейсы и практики монетизации мобильного трафика
Case study. Advertisers success in mobile economy
#MBC2016 Dan Ekstein, Supersonic Finding the Perfect Balance Between Rewarded...
#MBC2016 Кирилл Софронов, Remerge: Ретаргетинг в мобильных приложениях: опыт ...
#MBC2016 Илья Лагутин, Aitarget: Как эффективно продвигать мобильные приложен...
#MBC2016 Mark Ten, Sports.ru: Programmatic monetization of mobile apps
#MBC2016 Dvir Doron, Cedato: Mobile video - the new growth engine
#MBC2016 Bashir Modanov, NewBornTown: Globalized mobile ecosystem
Как настроить B2B продажи за границей из Украины
Как выжать еще больше из мобильного трафика?
AdExchange mediakit
Поймай меня, если сможешь
WannaBiz Fund
AdExchange mediakit
Clickky fraud presentation at Kinza 2015
МАРКЕТИНГОВЫЕ ЦЕЛИ И ЗАКУПКА ТРАФИКА
The trends of mobile in e-commerce, Mobilimes Serg Korneev
Инвестиции в mobile marketing, Алексей Тельнов iTech Capital, Mobile Beach Co...

Recently uploaded (20)

PPTX
Log360_SIEM_Solutions Overview PPT_Feb 2020.pptx
PDF
EN-Survey-Report-SAP-LeanIX-EA-Insights-2025.pdf
PPTX
CHAPTER 2 - PM Management and IT Context
PPTX
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
PPTX
Embracing Complexity in Serverless! GOTO Serverless Bengaluru
PPTX
Introduction to Artificial Intelligence
PPTX
history of c programming in notes for students .pptx
PPTX
Reimagine Home Health with the Power of Agentic AI​
PPTX
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
PDF
Which alternative to Crystal Reports is best for small or large businesses.pdf
PPTX
assetexplorer- product-overview - presentation
PPTX
Operating system designcfffgfgggggggvggggggggg
PDF
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
PDF
PTS Company Brochure 2025 (1).pdf.......
PDF
Upgrade and Innovation Strategies for SAP ERP Customers
PPTX
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
PDF
Designing Intelligence for the Shop Floor.pdf
PDF
Digital Systems & Binary Numbers (comprehensive )
PDF
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
PPTX
L1 - Introduction to python Backend.pptx
Log360_SIEM_Solutions Overview PPT_Feb 2020.pptx
EN-Survey-Report-SAP-LeanIX-EA-Insights-2025.pdf
CHAPTER 2 - PM Management and IT Context
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
Embracing Complexity in Serverless! GOTO Serverless Bengaluru
Introduction to Artificial Intelligence
history of c programming in notes for students .pptx
Reimagine Home Health with the Power of Agentic AI​
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
Which alternative to Crystal Reports is best for small or large businesses.pdf
assetexplorer- product-overview - presentation
Operating system designcfffgfgggggggvggggggggg
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
PTS Company Brochure 2025 (1).pdf.......
Upgrade and Innovation Strategies for SAP ERP Customers
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
Designing Intelligence for the Shop Floor.pdf
Digital Systems & Binary Numbers (comprehensive )
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
L1 - Introduction to python Backend.pptx

React + Redux. Best practices

  • 1. React + Redux Best practices
  • 3. 1. Create-react-app tool 1. Smart and Dumb components 2. Stateful and Stateless components 3. Binding 4. ShouldComponentUpdate 5. propTypes 6. Ducks pattern 7. Memoization (reselect) 8. Middlewares React + Redux. Best practices План
  • 5. React + Redux. Best practices
  • 6. Create React apps with no build configuration. npm install -g create-react-app create-react-app my-app cd my-app/ npm start React + Redux. Best practices
  • 7. React + Redux. Best practices
  • 8. To get started, edit src/App.js and save to reload React + Redux. Best practices
  • 9. package.json { "name": "my-app", "version": "0.1.0", "private": true, "dependencies": { "react": "^15.4.2", "react-dom": "^15.4.2" }, "devDependencies": { "react-scripts": "0.9.5" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" } } React + Redux. Best practices
  • 10. npm run eject Note: this is a one-way operation. Once you eject, you can’t go back! React + Redux. Best practices
  • 12. пример Dumb component React + Redux. Best practices
  • 13. пример Smart component React + Redux. Best practices
  • 14. Dumb import React from 'react'; export default class Dumb extends React.Component { selectNotificationItem = () => { const {id} = this.props; this.props.selectItem(id); } render () { const {text, time} = this.props; return ( <div className="item"> <span className="item__time">{time}</span> <p className="item__text" onClick={this.selectNotificationItem}>{text}</p> </div> ); } } React + Redux. Best practices
  • 15. <div className="list"> {list.map(item => { return ( <Dumb key={item.id} id={item.id} time={item.time} text={item.text} selectItem={this.handleSelectItem} /> ); })} </div> List.js React + Redux. Best practices
  • 16. Smartimport React from 'react'; import {connect} from 'react-redux'; import './App.css'; import Dumb from 'components/Dumb'; import {getNotificationsList, selectItem} from 'actions/notificatios'; class App extends React.Component { componentWillMount() { this.props.getNotificationsList(); } handleSelectItem = (id) => { this.props.selectItem(id); } render() { const {list} = this.props; return ( <div className="list"> {list.map(item => { return ( <Dumb key={item.id} id={item.id} time={item.time} text={item.text} selectItem={this.handleSelectItem} /> ); })} </div> ); } } const mapStateToProps = (state) => ({ list:state.notifications.list }); export default connect(mapStateToProps, { getNotificationsList, selectItem })(App); React + Redux. Best practices
  • 17. Глупые компоненты: 1. Не зависят от остальной части приложения, например от redux actions или stores 2. Получают данные и колбэки исключительно через props 3. Имеют свой css файл 4. Изредка имеют свой state 5. Могут использовать другие глупые компоненты Умные компоненты: 1. Оборачивает один или несколько глупых или умных компонентов 2. Хранит состояние стора и пробрасывает его как объекты в глупые компоненты 3. Вызывает redux actions и обеспечивает ими глупые компоненты в виде колбэков 4. Никогда не имеют собственных стилей 5. Редко сами выдают DOM, используйте глупые компоненты для макета React + Redux. Best practices
  • 18. Stateful and Stateless components React + Redux. Best practices
  • 19. Stateful import React from 'react'; export default class Counter extends React.Component { state = { count: 1 }; componentWillMount() { this.setState({ count: this.state.count + 1 }); } render () { const {count} = this.state; const {text} = this.props; return ( <div className="item"> <span className="item__count">{count}</span> <p className="item__text">{text}</p> </div> ); } } React + Redux. Best practices
  • 20. Stateless import React from 'react'; const Counter = ({addCount, text, count}) => { return ( <div className="item"> <span className="item__count" onClick={addCount}> {count} </span> <p className="item__text">{text}</p> </div> ); } export default Counter; React + Redux. Best practices
  • 22. React + Redux. Best practices Bind in Render handleSelectItem(id) { this.props.selectItem(id); } render() { const {list} = this.props; return ( <div className="list"> {list.map(item => { return ( <div className="list__item" onClick={this.handleSelectItem.bind(this, item.id)}> {item.text} </div> ); })} </div> ); }
  • 23. handleSelectItem(id) { this.props.selectItem(id); } render() { const {list} = this.props; return ( <div className="list"> {list.map(item => { return ( <div className="list__item" onClick={(item) => this.handleSelectItem(item.id)}> {item.text} </div> ); })} </div> ); } React + Redux. Best practices Arrow Function in Render
  • 24. Bind in Constructor constructor(props) { super(props); this.handleSelectItem = this.handleSelectItem.bind(this); } handleSelectItem(id) { this.props.selectItem(id); } render() { const {list} = this.props; return ( <div className="list"> {list.map(item => { return ( <div key={item.id} className="list__item"> <Dumb id={item.id} text={item.text} selectItem={this.handleSelectItem} /> </div> ); })} </div> ); } React + Redux. Best practices
  • 25. Arrow Function in Class Property handleSelectItem = (id) => { this.props.selectItem(id); } render() { const {list} = this.props; return ( <div className="list"> {list.map(item => { return ( <div key={item.id} className="list__item"> <Dumb id={item.id} text={item.text} selectItem={this.handleSelectItem} /> </div> ); })} </div> ); } React + Redux. Best practices
  • 27. shouldComponentUpdate(nextProps, nextState) { if (this.props.value !== nextProps.value) { return true; } if (this.state.count !== nextState.count) { return true; } return false; } component React + Redux. Best practices
  • 29. export default class Activation extends React.Component { static propTypes = { query: React.PropTypes.object, activatePublisher: React.PropTypes.func, activateAdvertiser: React.PropTypes.func, setActivationStatus: React.PropTypes.func, isFetching: React.PropTypes.bool.isRequired, activationStatus: React.PropTypes.bool.isRequired, language: React.PropTypes.string }; render() { const {query} = this.props; return( <div></div> ); } } components/Activation.js React + Redux. Best practices
  • 30. Console React + Redux. Best practices
  • 32. import { NOTIFICATIONS_GET_LIST_REQUEST, NOTIFICATIONS_GET_LIST_SUCCESS, NOTIFICATIONS_GET_LIST_FAILURE, NOTIFICATIONS_SELECT_ITEM } from 'constants'; export function getNotificationsList() { return { types: [NOTIFICATIONS_GET_LIST_REQUEST, NOTIFICATIONS_GET_LIST_SUCCESS, NOTIFICATIONS_GET_LIST_FAILURE], url: '/api/v1.0/notifications' } } export function selectItem(data) { return { type: NOTIFICATIONS_SELECT_ITEM, data } } actions/notifications.js React + Redux. Best practices
  • 33. import { NOTIFICATIONS_GET_LIST_REQUEST, NOTIFICATIONS_GET_LIST_SUCCESS, NOTIFICATIONS_GET_LIST_FAILURE, NOTIFICATIONS_SELECT_ITEM } from 'constants'; const initialState = { list: [], loading: false, selected: null }; export default (state = initialState, action = {}) => { switch (action.type) { case NOTIFICATIONS_GET_LIST_REQUEST: return { ...state, loading: true }; case NOTIFICATIONS_GET_LIST_SUCCESS: return { ...state, list: actions.result, loading: false }; case NOTIFICATIONS_GET_LIST_FAILURE: return { ...state, loading: false }; case NOTIFICATIONS_SELECT_ITEM: return { ...state, selected: action.data }; default: return state; } }; reducers/notifications.js React + Redux. Best practices
  • 34. export const NOTIFICATIONS_GET_LIST_REQUEST = 'NOTIFICATIONS_GET_LIST_REQUEST'; export const NOTIFICATIONS_GET_LIST_SUCCESS = 'NOTIFICATIONS_GET_LIST_SUCCESS'; export const NOTIFICATIONS_GET_LIST_FAILURE = 'NOTIFICATIONS_GET_LIST_FAILURE'; export const NOTIFICATIONS_SELECT_ITEM = 'NOTIFICATIONS_SELECT_ITEM'; constants/index.js React + Redux. Best practices
  • 35. // Actions const GET_LIST_REQUEST = 'my-app/notifications/GET_LIST_REQUEST'; const GET_LIST_SUCCESS = 'my-app/notifications/GET_LIST_SUCCESS'; const GET_LIST_FAILURE = 'my-app/notifications/GET_LIST_FAILURE'; const SELECT_ITEM = 'my-app/notifications/SELECT_ITEM'; // Reducer const initialState = { list: [], loading: false, selected: null }; export default function reducer(state = initialState, action = {}) => { switch (action.type) { case NOTIFICATIONS_GET_LIST_REQUEST: return { ...state, loading: true }; case NOTIFICATIONS_GET_LIST_SUCCESS: return { ...state, list: actions.result, loading: false }; React + Redux. Best practices modules/notifications.js
  • 36. case NOTIFICATIONS_GET_LIST_FAILURE: return { ...state, loading: false }; case NOTIFICATIONS_SELECT_ITEM: return { ...state, selected: action.data }; default: return state; } }; // Actions Creators export function getNotificationsList() { return { types: [GET_LIST_REQUEST, GET_LIST_SUCCESS, GET_LIST_FAILURE], url: '/api/v1.0/notifications' } } export function selectItem(data) { return { type: SELECT_ITEM, data } } React + Redux. Best practices
  • 37. 1. MUST export default a function called reducer() 2. MUST export its action creators as functions 3. MUST have action types in the form npm-module-or-app/reducer/ACTION_TYPE 4. MAY export its action types as UPPER_CASE, if an external reducer needs to listen for them, or if it is a published reusable library React + Redux. Best practices Requirements
  • 39. Memoization is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again React + Redux. Best practices
  • 41. import { createSelector } from 'reselect'; import {beautifyDate} from 'utils/date'; const data = (state) => state.notifications.data; export const notificationsSelector = createSelector( [data], (notifications) => { return notifications.reduce((list, item) => { const title = beautifyDate(item.date, 'DD.MM.YYYY'); if (!list[title]) { list[title] = [{...item}]; } else { list[title] = [...list[title], {...item}]; } return list; }, {}); } ); selectors/notifications.js React + Redux. Best practices
  • 42. import {notificationsSelector} from 'selectors/notifications'; const mapStateToProps = (state) => ({ list: notificationsSelector(state) }); export default connect(mapStateToProps, {})(List); containers/Notifications/index.js React + Redux. Best practices
  • 43. React + Redux. Best practices
  • 44. Basic middleware const customMiddleware = store => next => action => { if (action.type !== 'custom') { return next(action); } } export default customMiddleware; React + Redux. Best practices
  • 45. store/index.js import { createStore, applyMiddleware, } from 'redux' import reducer from './reducer' import customMiddleware from './customMiddleware' const store = createStore( reducer, applyMiddleware(customMiddleware) ); React + Redux. Best practices
  • 46. middleware/apiMiddleware.js import { push } from 'react-router-redux'; const apiMiddleware = (store) => (next) => (action) => { const { url, types, method, body, ...rest } = action; const { dispatch } = store; if (!types) { return next(action); } const [REQUEST, SUCCESS, FAILURE] = types; next({...rest, type: REQUEST}); return fetch(url, { method: method || 'GET', credentials: 'same-origin', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify(body) }).then((res) => { if (res.status >= 200 && res.status < 300) { return res.json(); } else { next({...rest, error: res.statusText, type: FAILURE}); if (res.status === 401) { window.localStorage.removeItem('user'); dispatch(push('/login')); } return Promise.reject(new Error(res.statusText)); } }).then(({result, ...rest}) => { next({...rest, result, type: SUCCESS}); }); }; export default apiMiddleware;
  • 47. export function getNotificationsList(data) { return { types: [NOTIFICATIONS_GET_LIST_REQUEST, NOTIFICATIONS_GET_LIST_SUCCESS, NOTIFICATIONS_GET_LIST_FAILURE], url: '/api/v1.0/notifications', method: POST body: data } } actions/notifications.js React + Redux. Best practices
  • 48. Делаем небольшие dumb компоненты, не боимся использовать connect, в пользу читаемости кода Для простого рендера используем stateless компоненты Всегда используем propTypes Контролируем перерендер с помощью shouldComponentUpdate Используем мемоизацию с помощью reselect Используем middleware Выводы React + Redux. Best practices