June 24, 2020

React/Redux

Глава 3: Функциональное программирование с применением JavaScript

-- ВОДА -- но интересная

Функциональные технологии используются все больше и больше в проектах JavaScript.

Умение разбираться в основных понятиях функционального программирования будет способствовать повышению ваших знаний по структурированию React-приложений.

Интерес к функциональному программированию появился в 1930-х годах с изобре- тением лямбда-исчисления, или λ-исчисления1. Появившиеся в XVII веке функции были частью исчисления. Они могли отправляться другим функциям в качестве аргументов или возвращаться из функций в качестве результатов. Более сложные функции, называемые функциями высшего порядка, могли манипулировать функ- циями и использовать их в качестве либо аргументов, либо результатов, либо и того и другого. В 1930-х годах Алонзо Черч (Alonzo Church), находясь в Принстоне, проводил эксперименты с этими функциями высшего порядка, в ходе которых изобрел лямбда-исчисления.

В конце 1950-х Джон Маккарти (John McCarthy) воспользовался понятиями, получаемыми из λ-исчислений, и применил их к новому языку программирования Lisp. В этом языке была реализована концепция функций высшего порядка и функ- ций, применяемых в качестве элементов или объектов первого класса (first-class members or first-class citizens). Функция считается элементом первого класса, когда мо- жет быть объявлена в качестве переменной и отправлена другим функциям в качестве аргумента. Такие функции могут быть даже возвращены из других функций.

-- конец воды --

Значение понятия функциональности

JavaScript поддерживает функциональное программирование, так как его функ- ции относятся к объектам первого класса. Это значит следующее: функции могут делать то же самое, что и переменные. В спецификацию ES6 добавляются усовершенствования языка, позволяющие подкреплять методы функционального программирования. К ним относятся стрелочные функции, промисы и оператор рас- пространения.

var createScream = function(logger) {
    return function(message) {
        logger(message.toUpperCase() + "!!!")
    }
}

тоже самое можно записать с помощью стрелочных функций

const createScream = logger => message =>
    logger(message.toUpperCase() + "!!!")

Отныне и впредь нужно будет обращать внимание на количество стрелок, ис- пользуемых при объявлении функции. Наличие более одной стрелки означает присутствие функции высшего порядка.

Сравнение императивности с декларативностью

Функциональное программирование является частью более обширной парадигмы: декларативного программирования. Оно представляет собой отдельный стиль программирования. При его соблюдении приложения структурированы таким образом: описание того, что должно случиться, приоритетнее определения того, как это должно случиться.

Чтобы разобраться в сути декларативного программирования, сравним его с импе- ративным программированием, или со стилем программирования, при котором вся забота сводится к тому, как достичь результатов с помощью кода.

Разберемся на примере

Рассмотрим типовую задачу: создание строкового значения, подходящего для использования в URL.

Императивный стиль

var string = "This is the midday show with Cheryl Waters";
var urlFriendly = "";
for (var i=0; i<string.length; i++) {
  if (string[i] === " ") {
    urlFriendly += "-";
  } else {
    urlFriendly += string[i];
  }
} 
console.log(urlFriendly);

Декларативный стиль

const string = "This is the mid day show with Cheryl Waters"
const urlFriendly = string.replace(/ /g, "-")
console.log(urlFriendly)

В декларативных программах легко разобраться, ведь происходящее описывается самим кодом.

Функциональные концепции

Неизменяемость

В функциональной программе данные являются неизменяемыми. Они никогда не изменяются.

Вместо изменения исходных структур данных создаются измененные копии этих структур, используемые взамен оригинала.

Пример

Неправильно

function rateColor(color, rating) {
  color.rating = rating
  return color
}
console.log(rateColor(color_lawn, 5).rating) // 5
console.log(color_lawn.rating)               // 5

Правильно

var rateColor = function(color, rating) {
   return Object.assign({}, color, {rating:rating})
}
console.log(rateColor(color_lawn, 5).rating) // 5
console.log(color_lawn.rating)               // 4

Функцию rateColor можно переписать так

const rateColor = (color, rating) => 
  ({
    ...color, 
    rating 
  }) 

Можно создать функцию, которая будет добавлять цвета к массиву, используя Array.push:

var addColor = function(title, colors) {
  colors.push({ title: title })
  return colors;
}
console.log(addColor("Glam Green", list).length)  // 4
console.log(list.length)                          // 4 

Но Array.push не является неизменяемой функцией. Это функция addColor из- меняет исходный массив путем добавления к нему еще одного элемента. Чтобы сохранить неизменяемость массива colors, нужно воспользоваться функцией Array.concat:

const addColor = (title, array) => array.concat({title})
console.log(addColor("Glam Green", list).length)    // 4
console.log(list.length)                            // 3

Или так

const addColor = (title, list) => [...list, {title}] 
Чистые функции

Чистой функцией называют функцию, которая возвращает значение, вычисляемое на основе ее аргументов

Привер чистой функции

const frederick = {
    name: "Frederick Douglass",
    canRead: false,
    canWrite: false
} 
const selfEducate = person =>
    ({
        ...person,
        canRead: true,
        canWrite: true
    })
console.log( selfEducate(frederick) )
console.log( frederick )
// {name: "Frederick Douglass", canRead: true, canWrite: true}
// {name: "Frederick Douglass", canRead: false, canWrite: false}

При написании функций старайтесь следо- вать трем правилам.

  • Функция должна получать как минимум один аргумент.
  • Функция должна возвращать значение или другую функцию.
  • Функция не должна вносить какие-либо изменения в переданные ей аргументы.
Преобразование данных

Преобразовываем объект schools в массив schools:

const schools = {
  "Yorktown": 10,
  "Washington & Lee": 2,
  "Wakefield": 5
} 
const schoolArray = Object.keys(schools).map(key => 
    ({ 
        name: key, 
        wins: schools[key]
    })
) 

Для преобразования массива в любое значение, включая число, строку, буле- во значение, объект или даже функцию, могут использоваться функции reduce и reduceRight.

const ages = [21,18,42,40,64,63,34];
const maxAge = ages.reduce((max, age) => {
    console.log(`${age} > ${max} = ${age > max}`);
    if (age > max) {
        return age
    } else {
        return max } 
}, 0) 

console.log('maxAge', maxAge);
// 21 > 0 = true
// 18 > 21 = false
// 42 > 21 = true
// 40 > 42 = false
// 64 > 42 = true
// 63 > 64 = false
// 34 > 64 = false
// maxAge 64

Если из предыдущей функции убрать инструкцию console.log и воспользоваться сокращенной формой инструкции if-else, то максимальное значение в масси- ве можно будет вычислить с применением следующего синтаксиса:

const max = ages.reduce(
    (max, value) => (value > max) ? value : max,
    0 
) 

Иногда нам нужно превратить массив в объект. В следующем примере функция reduce используется для преобразования массива, содержащего названия цветов, в хеш:

const colors = [
    {
      id: '-xekare',
      title: "rad red",
      rating: 3 
    }, 
    {
      id: '-jbwsof',
      title: "big blue",
      rating: 2 
    }, 
    {
      id: '-prigbj',
      title: "grizzly grey",
      rating: 5 
    }, 
    { 
      id: '-ryhbhsl',
      title: "banana",
      rating: 1
    } 
] 
const hashColors = colors.reduce(
    (hash, {id, title, rating}) => {
        hash[id] = {title, rating}
        return hash
    },
    {} 
) 

console.log(hashColors);
// { 
//   "-xekare": { title:"rad red", rating:3 },
//   "-jbwsof": { title:"big blue", rating:2 },
//   "-prigbj": { title:"grizzly grey", rating:5 },
//   "-ryhbhsl": { title:"banana", rating:1 }
// }

Применяя reduce, массивы можно даже преобразовать в совершенно другие массивы.

const colors = ["red", "red", "green", "blue", "green"];
const distinctColors = colors.reduce(
    (distinct, color) => 
      (distinct.indexOf(color) !== -1) ?
        distinct :
        [...distinct, color],
      []
)
console.log(distinctColors)
// ["red", "green", "blue"]
Функции высшего порядка

Array.map, Array.filter и Array.reduce — получают функции в качестве аргументов. Поэтому они считаются функциями высшего порядка

Рекурсия

Рекурсией называется технология создания функций, вызывающих самих себя.

Пример, вместо for используем рекурсивную функицю

const countdown = (value, fn) => {
  fn(value)
    return (value > 0) ? countdown(value-1, fn) : value 
} 

countdown(10, value => console.log(value));
// 10
// 9
// 8
// 7
// 6
// 5
// 4
// 3
// 2
// 1
// 0
Рекурсия должна использоваться вместо циклов везде, где только возможно, но не все движки JavaScript оптимизированы под большое количество рекур- сий. Слишком большое число рекурсий может привести к ошибкам JavaScript. Появления этих ошибок можно избежать, внедряя передовые методы очист- ки стека вызовов и «сплющивания» рекурсивных вызовов. В будущих движ- ках JavaScript ограничения по стеку вызовов планируется полностью снять.

Рекурсия — еще одна функциональная технология, хорошо работающая с асин- хронными процессами. Функции могут вызывать самих себя, если готовы к этому.

Рекурсию можно также использовать для последовательного обхода DOM HTML до тех пор, пока не будет найден элемент, не содержащий дочерних элементов. В следующем примере рекурсия применена для глубокого обхода объекта с целью извлечения вложенного значения:

var dan = {
    type: "person",
    data: {
      gender: "male",
      info: {
        id: 22,
        fullname: {
          first: "Dan",
          last: "Deacon"
        }
      } 
    } 
} 

const deepPick = (fields, object={}) => {
   const [first, ...remaining] = fields.split(".")
   return (remaining.length) ?
       deepPick(remaining.join("."), object[first]) : 
       object[first]
}

deepPick("type", dan);                     // "person"
deepPick("data.info.fullname.first", dan); // "Dan"

deepPick("data.info.fullname.first", dan); // "Deacon"
// Первая итерация
// first = "data"
// remaining.join(".") = "info.fullname.first"
// object[first] = { gender: "male", {info} }
// Вторая итерация
// first = "info"
// remaining.join(".") = "fullname.first"
// object[first] = {id: 22, {fullname}}
// Третья итерация
// first = "fullname"
// remaining.join("." = "first"
// object[first] = {first: "Dan", last: "Deacon" }
// Наконец...
// first = "first"
// remaining.length = 0
// object[first] = "Deacon"

Рекурсия является весьма эффективной и легко реализуемой функциональной технологией. Используйте ее вместо организации циклов везде, где только возможно.

Композиция

В функциональных программах логика разбивается на небольшие чистые функции, нацеленные на решение конкретных задач. Со временем эти небольшие функции требуется собрать воедино.

Составление в цепочку — один из методов создания композиции

Пример

const both = date => appendAMPM(civilianHours(date))

Функция both относится к функциям, пропускающим значение по конвейеру че- рез две отдельные функции. В данной записи можно запутаться если использовать не 2, а 20 функций

Более рациональный подход предусматривает создание функции высшего порядка, чем можно воспользоваться для составления композиций из функций и помещения их в более крупные функции.

const both = compose(
    civilianHours,
    appendAMPM 
) 

both(new Date())

Функция-композиция относится к функциям высшего порядка. Она получает функции в качестве аргументов, а возвращает одно значение.

const compose = (...fns) =>
  (arg) =>
    fns.reduce(
      (composed, f) => f(composed),
      arg 
) 

Функция compose получает в качестве аргументов функции и возвращает одну функцию. В этой реализации оператор распространения использован для пре- вращения этих функций-аргументов в массив по имени fns. Затем возвращается функция, ожидающая один аргумент, arg. Когда вызывается эта функция, массив fns выстраивается в конвейер, начинающийся с аргумента, который нужно про- пустить через функции. Аргумент становится исходным значением для composed, а затем при каждой итерации возвращается урезанная функция обратного вызова. Обратите внимание на то, что функция обратного вызова получает два аргумента: composed и функцию f. Каждая функция вызывается с аргументом composed, явля- ющимся результатом, полученным на выходе предыдущей функции. В конечном итоге будет вызвана последняя функция и возвращен последний результат.

А теперь все вместе

Дальше идет пример тикающих часов сделаный в императивном режиме и в декларативном, на gitHub где-то есть пример кода. Думаю после того как я прочту эту вату ещё раз понимание у меня будет больше.

Чистый React

React и ReactDOM. Первая представляет собой библиотеку для создания представлений, а вторая — библиотеку для фактического отображения пользовательского интерфейса в браузере.

React представляет собой библиотеку, разработанную для обновления за нас DOM браузера. Больше не придется переживать за сложности, сопряженные с созданием высокопроизводительных SPA, поскольку React может сделать все за нас. Используя React, мы не взаимодействуем с API DOM напрямую. Вместо этого мы имеем дело с виртуальной DOM или с набором инструкций, применяемых React для построения пользовательского интерфейса и организации взаимодействия с браузером.

Виртуальная DOM составлена из элементов React, которые концептуально очень похожи на элементы HTML, но фактически являются объектами JavaScript. Ра- ботать непосредственно с последними гораздо быстрее, чем работать с API DOM. Мы вносим изменения в объект JavaScript, в виртуальную DOM, и React отобра- жает эти изменения для нас, используя API DOM наиболее эффективным образом.

Элементы React

DOM браузера составлена из элементов данной модели. По аналогии с этим DOM библиотеки React составлена из элементов React. Элементы DOM и элементы React могут выглядеть одинаково, но на самом деле они совершенно разные. По- следние являются описанием того, как должен выглядеть настоящий элемент DOM. Иными словами, элементы React представляют собой инструкции того, как должна быть создана DOM браузера.

React.createElement("h1", null, "Baked Salmon")

Выведет

<h1>Baked Salmon</h1>
React.createElement("h1",
  {id: "recipe-0", 'data-type': "title"}, 
  "Baked Salmon"
)

Выведет

<h1 data-reactroot id="recipe-0" data-type="title">Baked Salmon</h1>

ReactDOM

В библиотеке ReactDOM содержатся инструменты, необходимые для отображе- ния элементов React в браузере.

Дочерние элементы

ReactDOM позволяет отображать в DOM единственный элемент.

React отображает дочерние элементы с помощью свойства props.children.

В этой главе компоненты React создавались с помощью функций createElement и createFactory.

React с JSX

...... ВОДА ......

Введение в Webpack

В качестве одного из лидирующих инструментов для создания модулей CommonJS стала рассматриваться среда Webpack.

Модульная сборка имеет два основных преимущества: модульность и высокую сетевую производительность.

Модульность позволяет разбивать исходный код на части, или модули, с которыми проще работать, особенно когда разработка приложения ведется в команде.

Сетевая производительность повышается за счет того, что в браузер требуется загрузить только одну зависимость в виде пакета. Все теги script делают HTTP- запросы, каждый из которых влечет крайне невыгодные задержки. Сбор всех зави- симостей в один файл позволяет все загружать за один HTTP-запрос, избегая тем самым излишних задержек.

Кроме транспиляции, Webpack может выполнять следующие задачи.

  • Разбиение кода — на разные фрагменты, которые могут загру- жаться по мере необходимости.
  • Минификация — избавление от пробельных символов, разбиений строк, слишком длинных имен переменных и ненужного кода с целью уменьшения размера файла.
  • Прогон функций (feature flagging) — отправка кода при тестировании его свойств в одну или несколько сред (но не во все).
  • Замена модулей без полной перезагрузки (hot module replacement, HMR) — отслеживание изменения в исходном коде. Немедленная замена только обновленных модулей.

Загрузчик представляет собой функцию, управляющую преобразованиями, ко- торым нужно подвергнуть код в ходе сборки.

Свойства, состояние и дерево компонентов

Еще один способ повышения качества компонентов заключается в присваивании свойствам значений по умолчанию

getDefaultProps() {
  return {
    ingredients: 0,
    steps: 0,
    title: "[recipe]"
  } 
}

Встроенные в React средства проверки хорошо подходят для того, чтобы убедиться в правильной востребованности и типизированности ваших переменных. Но есть экземпляры, нуждающиеся в более серьезной проверке. Например, нужно будет убедиться в том, что число находится в указанном диапазоне или значение содер- жит указанную строку. Для таких случаев React позволяет вам создать собствен- ную проверку.

Настраиваемая проверка реализуется в React с помощью функции. Она должна возвратить либо ошибку, когда не соблюдаются указанные требования по про- верке, либо null, когда свойство соответствует требованиям.

propTypes: {
  ingredients: PropTypes.number, 
  steps: PropTypes.number, 
  title: (props, propName) => 
      (typeof props[propName] !== 'string') ?
        new Error("A title must be a string") :
        (props[propName].length > 20) ?
          new Error(`title is over 20 characters`) :
          null
}

Ссылки

Ссылки (refs) представляют собой функциональную возможность, позволяющую компонентам React взаимодействовать с дочерними элементами.

Обратный поток данных

Устоявшимся решением для сбора данных из компонента React является использование обратного потока данных1. Действие похоже на двустороннюю привязку данных, которая иногда и фигурирует в описаниях. Для этого привлекается отправка компоненту в виде свойства функции обратного вызова, которую компонент может использовать для передачи данных обратно в качестве аргументов. Это называется обратным потоком данных, поскольку функция отправляется компоненту в качестве свойства, а компонент отправляет данные обратно в качестве аргументов функции.

Управление состоянием React

Состояние представляет данные, которые при желании можно изменить внутри компонента.

В большинстве случаев нужно избегать установки state из props.

Самое важное, что можно сделать с целью облегчить понимание вашей програм- мы, — это свести к необходимому минимуму количество компонентов, использу- ющих состояние.

Во многих приложениях React есть возможность сгруппировать все данные состоя- ний в корневом компоненте. Данные состояний можно передать вниз по дереву компонентов через свойства, а вверх по дереву к корневому компоненту — через двустороннюю привязку функций. В результате все состояние вашего приложения в целом будет находиться в одном месте. Часто это называют «единым источником истины».

Презентационные компоненты отвечают только за образ, создаваемый приложением на экране. Они лишь отображают элементы DOM или другие презен- тационные компоненты.

Состояние передается из компонента App дочерним компонентам в качестве свойств
Передача данных вверх корневому компоненту при возникновении событий пользовательского интерфейса

Усовершенствование компонентов

Приложения можно совершенствовать с помощью библиотек JavaScript, создан- ных сторонними разработчиками, или путем запроса внутренних данных. Но пре- жде нужно понять, как работать с жизненным циклом компонента: серией методов, которые могут вызываться при каждой установке или обновлении компонента.

Жизненные циклы компонентов

Жизненный цикл компонента состоит из методов, последовательно вызываемых при установке или обновлении компонента. Частью жизненного цикла компонента является сам метод отображения render. Существует два основных вида цикла: жизненный цикл установки и жизненный цикл обновления.

Жизненный цикл установки

Жизненный цикл установки состоит из методов, вызываемых при установке компонента или при его удалении с экрана.

Конструкторы класса
С технической точкси зрения конструктор не метод жизненного цикла. Он включен в перечень из-за того, что используется для инициализации компо- нента (при которой инициализируется состояние). Кроме того, конструктор всегда является первой функцией, вызываемой при установке компонента.

После получения свойств и инициализации состояния вызывается метод componentWillMount. Он вызывается до отображения DOM и может быть использован для инициализации библиотек, созданных сторонними разработчиками, запуска анимаций, запроса данных или выполнения любых дополнительных настроек, которые потребуются перед отображением компонента на экране. Из этого мето- да можно вызвать метод setState, чтобы изменить состояние компонента непо- средственно перед тем, как он будет первоначально отображен на экране.

Использование setState в componentWillMount
Вызов метода setState перед отображением компонента на экране не за- пустит жизненный цикл обновления, в отличие от вызова после отображе- ния. Если этот метод вызывается внутри асинхронной функции обратного вызова, определенной в методе componentWillMount, то он будет вызван после отображения компонента на экране и запустит жизненный цикл об- новления.

componentDidMount - вызывается сразу же после отображения компонента на экране

componentWillUnmount - непосредственно перед удалением его с экрана.

Метод componentDidMount является еще одним местом, откуда удобно отправлять запросы к API. Он вызывается после отображения компонента на экране, поэтому любые вызовы setState из данного метода приведут к запуску жизненного цикла обновления и к повторному отображению компонента на экране.

Метод componentDidMount также хорошо подходит для инициализации любого кода JavaScript, созданного сторонними разработчиками, которому требуется DOM. На- пример, может потребоваться внедрить библиотеку перетаскивания объектов или библиотеку, обрабатывающую события прикосновения к экрану. Обычно таким библиотекам перед их инициализацией требуется DOM.

Жизненный цикл обновления

Жизненный цикл обновления представляет собой последовательность методов, вызываемых при изменении состояния компонента или при получении от родительского компонента новых свойств.

В число методов жизненного цикла обновления входят:

  • componentWillReceiveProps(nextProps) — вызывается только в случае пере- дачи компоненту новых свойств; единственный метод, в котором может быть вызван метод setState;
  • shouldComponentUpdate(nextProps, nextState) — привратник жизненного цикла обновления: предикат, способный отменить обновление; может использоваться для повышения производительности, разрешая только необходимые обновления;
  • componentWillUpdate(nextProps, nextState) — вызывается непосредственно перед обновлением компонента; похож на метод componentWillMount, но вызывается только перед выполнением каждого обновления;
  • componentDidUpdate(prevProps, prevState) — вызывается сразу же после выполнения обновления, после вызова метода отображения render; похож на метод componentDidMount, но вызывается только после каждого обновления.

React.Children

API React.Children предоставляет способ работы с дочерними компонентами отдельно взятого компонента. Он позволяет вести их подсчет, выполнять их ото- бражение, циклический обход или превращать props.children в массив. Он также позволяет с помощью React.Children.only убеждаться, что на экран выводится единственный дочерний компонент.

import { Children, PropTypes } from 'react' 
import { render } from 'react-dom' 

const Display = ({ ifTruthy=true, children }) =>
    (ifTruthy) ? 
      Children.only(children) : 
      null 

const age = 22 
render(
    <Display ifTruthy={age >= 21}>
        <h1>You can enter</h1>
    </Display>, 
    document.getElementById('react-container')
)
React с jQuery использовать не рекомендуется.

Компоненты высшего порядка

Компонент высшего порядка (higher-order component, HOC) представляет собой простую функцию, получающую в виде аргумента один компонент React и воз- вращающую другой.

HOC позволяют заключать один компонент в другой. Родительский компонент может сохранять состояние или содержать функциональность, пригодную для передачи вниз составному компоненту в виде свойств.

Управление состоянием за пределами React

Flux — модель конструирования, разработанная в Facebook с целью организовать однонаправленный поток данных.

Flux предоставляет способ построения веб-приложений, дополняющий работу React. Если конкретнее, то Flux дает способ предоставления данных, которые React будет использовать для создания пользовательского интерфейса.

Во Flux данные состояния приложения управляются за пределами компонентов React в хранилищах (stores).
Если пользователь должен взаимодействовать с веб-страницей, то для представления пользовательского запроса будет создано действие (action).
Инструкции отправляются с помощью центрального управляющего элемента под названием «диспетчер» (dispatcher).
Он сконструирован с целью выстраивания действий в очередь и отправки их в соответствующее хранилище.
При получении действия хранилище использует инструкции для внесения изменений в состояние и обновление представления.
Получается односторонний поток данных: действия проходят к диспетчеру, оттуда к хранилищу и, наконец, к представлению.

stores и action - неизменяемы

Представление - компоненты React не имеющие состояния

Действия - это инструкции и данные, которые хранилище будет использовать для изменения состояния. По сути это объекты, как минимум с одним свойством type.

Создатели действий - это функции, благодаря которым можно абсрагировать всевозможные детали, необходимые для создания дейсвия.

Диспетчер - получает действие, упаковывает в него информацию о том, где оно было создано, и отправляет его в соответствующее хранилище или хранилища, которые займутся выполнением действия.

Хранилища - это объекты, в которых содержатся логика и данные состояния приложения.

Redux

Мы говорили, что Redux является Flux-подобной библиотекой, но это не совсем Flux. В ней есть действия, создатели действий, хранилище и объекты действий, используемые для изменения состояния. Redux немного упрощает концепции Flux за счет удаления диспетчера и представления состояния приложения с помощью единственного неизменяемого объекта. В Redux также введены преобразователи (reducers), не являющиеся составной частью модели Flux. Преобразователи представляют собой чистые функции, возвращающие новое состояние на основе текущего состояния и действия: (state, action) => newState.

Состояние

Redux требует, чтобы все состояние хранилось в одном неизменяемом объекте

При использовании Redux все управление состоянием полностью удаляется из React. Состоянием будет управлять Redux.

При создании Redux-приложений состояние является первым, о чем нужно по- думать. Постарайтесь определить его в одном объекте. Обычно рекомендуется составлять черновой JSON-проект вашего дерева состояния с местами, предна- значенными для заполнения данными.

Действия

Действия — литералы JavaScript, предоставляющие инструкции, необходимые для внесения изменений в состояние. Большинству изменений состояния также нужны данные. Какие именно записи следует удалить? Какую новую информацию нужно предоставить в новой записи?

Пример: Действие RATE_COLOR

{
    type: "RATE_COLOR",
    id: "a5685c39-6bdc-4727-9188-6c9a00bf7f95",
    rating: 4
}

Преобразователи

В Redux модульность достигается за счет функций. Они используются для обновления частей дерева состояния. Эти функции называются преобразователями (reducers).

Преобразователи представляют собой функции, которые получают текущее состояние и действие в виде аргументов и используют их для создания и возвращения нового состояния.

Преобрахователь возвращает тот тип, что в начале принял. Если получил массив, то возвращает массив, если объект, то возвращает объект и т.д

Каждый преобразователь предназначен для обработки только тех действий, которые необходимы для обновления его части дерева состояния.

Каждый преобразователь состоит из функции или сводится в одну функцию преобразователя, которая будет использовать хранилище.

Дерево преобразователей органайзера цветов
Объединение преобразователей не требование, а просто рекомендация.

Преобразователи всегда должны что-нибудь возвращать. Если по какой-либо причине данный преобразователь был вызван с неопознанным действием, то как вариант по умолчанию (default) им будет возвращено текущее состояние.

В преобразователях не должно быть никаких побочных эффектов
Преобразователи должны быть предсказуемыми. Они используются про- сто для управления данными состояния. Обратите внимание: в предыдущем примере метка времени и идентификаторы сгенерированы до отправки дей- ствия преобразователю. Генерирование случайных данных, вызов функций API и другие асинхронные процессы должны обрабатываться за пределами преобразователей. Для них неизменно рекомендуется избегать изменения состояния и побочных эффектов.

Итак, обновление состояния выполняется преобразователями, которые являются чистыми функциями, получающими в качестве первого аргумента состояние, а в качестве второго аргумента — действие. Преобразователи не вызывают побоч- ных эффектов и должны рассматривать свои аргументы в качестве неизменяемых данных. Модульность в Redux достигается с помощью преобразователей. В конечном итоге преобразователи сводятся в один, то есть в функцию, которая может обновить все дерево состояния.

Хранилище

В Redux хранилищем считается то место, где хранятся данные состояния приложения и обрабатываются все обновления состояния.

Хранилище занимается обновлениями состояния, пропуская текущее состояние и действие через единый преобразователь.

Создатели действий

Объекты действий представляют собой простые литералы JavaScript. Создатели действий являются функциями, которые создают и возвращают эти литералы.

Создатели действий хороши тем, что предоставляют место для инкапсуляции всей логики, необходимой для успешного создания действия. В создатель действий нужно помещать всю логику обмена данными с серверными API. Используя создатель, можно задействовать логику асинхронного обмена данными, такую как запрос данных или вызов функций API.

Функции промежуточного звена (middleware)

middleware - служат в качестве соединителя между различными уровнями или различными частями программного средства.

В Redux тоже есть связующий код. Он работает в конвейере диспетчеризации, действующем в хранилище. Связующий код в Redux состоит из серии функций, выполняемых поочередно в процессе диспетчеризации действия.

Конвейер связующего кода HTTP-запроса

Эти функции высшего порядка позволяют вставлять функциональные средства до или после диспетчеризации действий и обновления состояния. Все функции связующего кода выполняются последовательно.

Функции связующего кода выполняются последовательно

Фабрикой называется функция, ко- торая управляет процессом создания хранилищ.

React Redux

Следующая часть книги, больше похожа на справочник примеров с объяснениями. А цель данной записи как раз вот чтобы больше описать теорию, для подготовки к резюме. Дальше я буду делать свой проект, а к книге вернусь, когда почувстую в ней необходимость.