23.01.2022

Редуктор как работает: Редуктор: устройство, принцип работы, виды, назначение

Содержание

устройство, принцип работы, виды, назначение

Редуктор – механизм, изменяющий крутящий момент и мощность двигателя, присутствует практически в любой машине и станке. Он является частью трансмиссии автомобиля и регулирует с высокой точностью перемещение в точных приборах. Что такое редуктор с технической точки зрения? Это одно или несколько зубчатых зацеплений, взаимодействующих между собой и понижающих количество оборотов двигателя до приемлемой скорости вращения исполняющего узла. Вместо ведущей шестерни может быть червяк.

Устройство и принцип работы

Редуктор без дополнений газовый или гидравлический, подразумевает механическое устройство для изменения угловой скорости и крутящего момента. Он работает по принципу Золотого правила, когда передаваемая вращением мощность практически не изменяется, уменьшается на КПД.

Устройство

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

Шестерни и колеса неподвижно закреплены на валах или изготовлены совместно с ними. В корпусе может быть от одной до нескольких пар зубчатых зацеплений. На сборочном чертеже редуктора хорошо видно его устройство и составные части:

  • корпус;
  • крышка корпуса;
  • пары в зацеплении;
  • валы;
  • подшипники;
  • уплотнительные кольца;
  • крышки.

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

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

Принцип действия

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

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

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

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

Маркировка

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

  • цилиндрическое – Ц;
  • червячное – Ч;
  • коническое – К;
  • глобоидное – Г;
  • волновые – В;
  • планетарное – П.

Комбинированные модели обозначаются несколькими буквами, начиная с первой пары:

  • цилиндрически-червячные – ЦЧ;
  • червячно-цилиндрические – ЧЦ;
  • конически-цилиндрические – КЦ.

Количество передач данного вида указывается цифрой перед буквой.

Горизонтальное расположение считается нормой и не имеет своего обозначения. Для вертикального узла после обозначения типа передач ставится буква В. Б – означает быстроходную модель. За ним ставится условное числовое обозначение варианта сборки.

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

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

Например: 1Ц2У-250-31,5-22-М-У2. Двухступенчатый цилиндрический с горизонтальным расположением. Межцентровое расстояние валов тихоходной ступени 250 мм, передаточное число 31,5. Вариант сборки узла 22, хвостовик по типу муфты, климатическое исполнение соответствует ГОСТ 15150-69.

Скачать ГОСТ 15150-69

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

Технические характеристики

Редуктора отличаются внешне по размерам и форме. Внутреннее строение разнообразное. Объединяет их всех перечень технических характеристик, по которым они подбираются на различные машины и станки. К основным параметрам редуктора относятся:

  • передаточное число;
  • передаточное отношение;
  • значение крутящего момента редуктора;
  • расположение;
  • количество ступеней;
  • крутящий момент.

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

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

Передаточное число

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

U = Z2/Z1;

где U – передаточное число;

Z1 число зубьев шестерни;

Z2 число зубьев зубчатого колеса.

Модуль зубьев шестерни и колеса одинаковый. Их количество напрямую зависит от диаметра. Поэтому можно использовать формулу:

U = D2/D1;

Где D2 и D1 диаметры колеса и шестерни соответственно.

Расчет общего передаточного момента определяется как произведение передаточных чисел всех пар:

Uр = U1× U2× … × Un;

Где Uр передаточное число;

U1, U2, Un передаточные числа зубчатых пар.

При расчете передаточного числа берется отношение количества зубьев колеса и заходов червяка.

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

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

Передаточное отношение

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

где U12 – передаточное отношение;

W1 и W2 – угловые скорости;

n1 и n2 – частота вращения.

Знак «–» указывает на обратное направление вращения колеса и вала, на котором оно находится. При нечетном количестве передач ведомое колесо крутится в противоположном направлении по отношению к ведущему, навстречу ему. При четном количестве зацеплений конических колес вращение обоих валов происходит в одном направлении. Заставить его крутится в нужную сторону можно установкой промежуточной детали – паразитки. У нее количество зубьев как у шестерни. Паразитка изменяет только направление вращения. Все остальные характеристики остаются прежними.

Крутящий момент

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

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

Назначение механизма

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

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

Скорость вращения электродвигателя может достигать 1500 об/мин. Для работы станка оборудования она не подходит. При этом, если к шкиву мотора напрямую прикрепить груз, он не сможет сдвинуть его с места.

Функции узла, уменьшить скорость вращения в десятки раз и настолько же увеличить крутящий момент – усилие, с которым машина будет совершать работу.

Виды редукторов

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

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

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

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

По типу зубчатого зацепления и форме шестерни, они делятся:

  • цилиндрические;
  • конические;
  • червячные;
  • планетарные;
  • комбинированные;
  • волновые.

Комбинированные модели могут иметь различные типу зубчатых зацеплений.

Цилиндрические

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

По форме зуба цилиндрические модели делятся:

  • прямозубые;
  • косозубые;
  • шевронные.

По кинематической схеме они бывают прямолинейные и разветвленные.

Прямой зуб имеет закругленную поверхность, способствующую максимально возможной площади контакта. При зацеплении зубья контактируют по всей длине. Трение сводится к минимуму. КПД прямозубого зацепления наиболее высокое, 99%.

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

Недостаток в сильном шуме во время работы и малой мощности. Чтобы предать большое усилие, колеса надо делать широкими, крупногабаритными.

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

Площадь трения по эвольвенте больше, детали греются. КПД косозубого зацепления 98% и ниже. Изготовление деталей с косым зубом сложнее, особенно фрезеровка зубьев. Требуется большая точность при настройке режущего инструмента. Наклонное положение зуба создает дополнительные осевые нагрузки на подшипники и сокращает срок их работы.

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

Работают шевронные зацепления тихо. Недостаток в сложной и длительной технологии нарезания зубьев.

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

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

Конические

Шестерня и колесо имеют коническую поверхность. Валы расположены под углом. Зуб на шестерне прямой и радиальный. Часто конические передачи используются в комбинированных или понижающих узлах. Направление вращения возможно в любую сторону. В качестве ведущего может выступать колесо.

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

Червячный

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

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

Червячные редукторы создают с одной и двумя передачами. Часто делают комбинированные с коническими зацеплениями.

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

Недостаток в низком КПД и сильном нагреве во время работы. необходимо делать систему охлаждения.

Планетарный

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

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

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

Комбинированные

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

Мотор-редуктор – собранные в одном корпусе двигатель и передаточный узел. Привод обычно изготавливается с коническими или червячными парами. Количество передач одна и две.

В волновых моделях для вращения применяют колебания расположенной внутри колеса шестерни. Широкого распространения модель пока не получила.

Рекомендации по выбору

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

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

Крепление осуществляется с помощью фланца непосредственно к валу двигателя и с помощью отверстий в подошве устанавливается на платформу.

В маркировке указано межцентровое расстояние между валами. Этот размер имеет конструктивное значение при установке узла и соединения его с двигателем и валом рабочего механизма.

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

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

Распространенные неисправности

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

Основная неисправность редуктора связана с его перегревом. Это происходит при отсутствии смазки и использовании масел других марок. В противном случае агрегат перегревается, зубчатое зацепление может заклинить.

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

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

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

устройство, принцип действия, варианты исполнения

Содержание:

Устройство волнового редуктора

В состав волнового редуктора входят три основных части: генератор волны, жёсткое колесо и гибкое колесо. Генератор волны в самом распространённом варианте выполняется в виде шарикоподшипника с тонкими гибкими стенками. Он устанавливается на эллиптическую втулку, и сам принимает форму эллипса. Сборка из этих двух деталей и является генератором волны. Гибкое колесо – это деталь специфическая для волнового редуктора. Оно представляет собой тонкостенное зубчатое колесо с наружным зубом. Основная рабочая поверхность этого колеса имеет форму цилиндра. Материал и толщина гибкого колеса подобраны так, чтобы оно могло постоянно испытывать упругие деформации, не теряя своих свойств. Конструкция жёсткого колеса проще чем других частей волнового редуктора. Это обычное зубчатое колесо с внутренним зубом. Его размеры подобраны так чтобы обеспечивать достаточно большую жёсткость при рабочих нагрузках.

 
генератор волны гибкое колесо жесткое колесо

При сборке волнового редуктора гибкое колесо устанавливают на генератор волны, в результате чего оно тоже принимает эллиптическую форму. Далее на гибкое колесо устанавливается жёсткое колесо. Поскольку гибкое колесо в процессе сборки приняло эллиптическую форму, то его зацепление с жёстким колесом происходит только на двух участках. Расположены они вдоль большой полуоси генератора волны и в сумме занимают около 40% окружности. За пределами этих участков зацепления зубьев жёсткого и гибкого колеса не происходит. Гибкое колесо имеет меньше зубьев чем жёсткое колесо. Чаще всего эта разница составляет 2 зуба, однако есть другие варианты конструкции волновых редукторов, где эта разница больше.

Принцип работы

По мере поворота генератора волны, зубья гибкого и жёсткого колёс поочерёдно начинают входить в зацепление. Участки зацепления зубьев в результате начинают смещаться в том же направлении в котором вращается генератор волны. Как только генератор волны совершит полный оборот, гибкое и жёсткое колесо окажутся смещены друг относительно друга на те самые 2 зуба, которые составляют разницу в количестве зубьев между этими колёсами. Это означает что гибкое и жёсткое колеса повернулись друг относительно друга со скоростью, существенно меньшей чем скорость с которой вращался генератор волны. Генератор волны вращаясь достаточно быстро позволяет получить сравнительно медленное вращение гибкого колеса относительно жёсткого – то есть механизм работает как редуктор. Коэффициент редукции такого волнового редуктора зависит от разницы зубьев между гибким и жёстким кольцом, а также от количества зубьев у жёсткого кольца.

Варианты включения

Когда волновой редуктор используется так как показано выше, то генератор волны используется как вход, гибкое колесо – это выход, а жёсткое колесо остаётся неподвижным. Волновой редуктор можно использовать и по-другому, если зафиксировать не жёсткое колесо, а генератор волны или гибкое колесо; входом и выходом в таком случае могут быть из двух оставшихся элементов редуктора. При различных вариантах включения волновая передача может быть использована как для понижения скорости, так и для её повышения. Передаточное число редуктора при этом также изменится. Может измениться и направление вращения выходного элемента относительно входного.

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

Варианты исполнения компактных волновых редукторов

За время, прошедшее с момента изобретения волнового редуктора, было придумано много вариантов его конструкции. И вариант, когда шарикоподшипник эллиптической формы используется как генератор волны, не исчерпывает всех возможных вариантов конструкции. Существуют и другие варианты. Например, генератор волны может быть выполнен в виде коромысла с роликами на его концах. Или в виде планетарных шестерён, установленных на водило, которые зацепляются с зубьями, сделанными с внутренней стороны гибкого колеса. Помимо этого, генератор волны может быть выполнен в виде детали более сложной формы, создающей на гибком кольце 3 или 4 зоны зацепления (вместо двух зон в самом простом случае).

Гибкое колесо также может иметь разную форму. На практике чаще всего встречаются три формы: «кастрюля», «шляпа» и цилиндр. Отличия между ними заключаются в удобстве использования.

Гибкое колесо типа «кастрюля» Гибкое колесо типа «цилиндр» Гибкое колесо типа «шляпа»

 

Основные отличия волнового редуктора

  • Большое передаточное число для одноступенчатого редуктора: до 320:1 в серийно выпускаемых изделиях
  • Большое количество зубьев, которые находятся в одновременном зацеплении
  • Высокая точность
  • Большой момент нагрузки в расчёте на единицу объёма или на единицу массы
  • Отсутствие маленьких передаточных чисел (менее 30:1)
  • Простая конструкция
  • Высокая надёжность
  • Простая передача вращения в другую среду
  • Полый вал
  • Жёсткость на скручивание ограничена
  • Короткая осевая длина

Практические преимущества волновых редукторов

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

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

Читать дальше:

Как работает редуктор или мотор-редуктор: Статьи

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

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

Ниже приведены основные принципы работы редуктора или мотор-редуктора:

Предположим, что колесо «А» на рис.1 имеет диаметр 5 см. Тогда его периметр будет 5 х 3,1416 = 15,71 см. Периметр — это общая длина колесной рамки. Колесо «B» диаметром 15 см и периметром 47,13 см (15 x 3,1416) контактирует с периметром колеса «A» (рис. 2).

КОНЦЕПЦИЯ ПЕРЕДАТОЧНОГО ЧИСЛА В МОТОР-РЕДУКТОРЕ

На рис. 3, когда колесо «А» поворачивается, оно заставляет вращаться колесо «В», но может случиться так, что на каждые три оборота, которые делает «А», колесо «В» сделает только один оборот, то есть диаметр «B» делится на диаметр «A» (15/5 = 3). Это число 3 будет передаточным числом этого элементарного редуктора или мотор-редуктора и обозначено как 3: 1.

С помощью этой простой комбинации стало возможным снизить скорость вращения колеса «B» до одной трети скорости вращения колеса «A». Если мы привяжем другие дополнительные колеса к комбинации колес, описанной выше, то каждый раз мы будем достигать все более низкой и меньшей скорости туда, где это необходимо для приложения, и может быть 6: 1, 30: 1, 100: 1 или даже выше. для достижения очень малых скоростей, которые могут потребоваться, и что, например, колесо «А» должно было повернуться сотни раз, чтобы последнее колесо повернулось только один раз. В этом случае у нас будет мотор-редуктор с несколькими редукторами, понимаемыми как один редуктор на пару колес. С шестью колесами у нас будет три зубчатых колеса.

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

КОНЦЕПЦИЯ КРУТЯЩЕГО МОМЕНТА В МОТОР-РЕДУКТОРЕ

«Крутящий момент» — это сила вращения, например, сила поворота выходного вала мотор-редуктора. Вращающая сила выражается в следующей системе — килограммы-метры, фунт-футы, фунт-дюймы, ньютон-метры.

Электродвигатель имеет определенную мощность в кВт и определенную рабочую скорость вращения выходного вала, например, 1800 оборотов в минуту (об / мин). Эти две характеристики: скорость и мощность несут в себе определенный «крутящий момент», который может высвободить двигатель. Именно «крутящий момент» позволит нам вращать определенную нагрузку, чем выше «крутящий момент». тем больший груз мы сможем перевернуть. Насколько быстро мы сможем это сделать, будет зависеть от мощности мотор-редуктора. Эти две характеристики взаимосвязаны и зависят друг от друга.

Эта комбинация мощности, крутящего момента и скорости двигателя или мотор-редуктора определяется следующей формулой:

Как видно из формулы, для данной мощности, чем ниже конечная скорость вращения вала мотор-редуктора, тем выше будет крутящий момент, даже если мощность останется прежней. И наоборот: чем выше конечная скорость редуктора или мотор-редуктора, тем ниже будет крутящий момент, даже если мощность такая же.
Давайте посчитаем выходной крутящий момент, который может обеспечить мотор-редуктор мощностью 5 кВт с передаточным числом 59: 1. Двигатель 4-полюсный, с номинальной частотой вращения 1750 об / мин.
 
Если двигатель имеет выходную мощность 1750 об / мин, а редуктор имеет передаточное число 59: 1, это означает, что выходная скорость будет:

Скорость на выходе из редуктора = 1750/59 = 29,66 об / мин.

Тогда доступная пара будет:

Располагаемый крутящий момент 120,7 кгс.

Это означает, что у мотор-редуктора будет достаточно крутящего момента, чтобы повернуть груз весом 120,7 кг, подвешенный на 1-метровом рычаге, привинченном к выходному валу.

РАСЧЕТ НЕОБХОДИМОЙ МОЩНОСТИ МОТОР-РЕДУКТОРА

И наоборот, если мы знаем, какой «крутящий момент» нам нужен для перемещения нагрузки и с какой скоростью вращения требуемая работа выполняется надлежащим образом, то мы можем рассчитать необходимый мотор-редуктор и мощность двигателя. Например, если требуемый крутящий момент составляет 125 кг-м и нам нужно, чтобы он вращался со скоростью 40 об / мин, тогда необходимый мотор-редуктор будет:

Расчет передаточного отношения уменьшения = 1750/40 = 43,75: 1

Решение по формуле:

Требуется двигатель мощностью 6,98 кВт. Так как серийного мотора такой мощности нет, берем ближайший — 7,5 кВт
Требуется уменьшение 43,75: 1. Поскольку в продаже нет коробки передач с точно таким передаточным числом, мы выбираем ближайшую, которая имеет передаточное число 43: 1.
 
Таким образом, мотор-редуктор должен соответствовать следующим характеристикам:
Мотор-редуктор 7,5 кВт, передаточное отношение 43: 1 при выходной скорости 40,7 об / мин

Принцип работы планетарно-цевочного редуктора (циклоидного, циклоидального редуктора)

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

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

  • Высокий, до 90%, коэффициент полезного действия. А, значит, малые потери на трение и нагрев;
  • Возможность реализации как очень низких, так и очень высоких передаточных отношений в одной ступени;
  • Минимально возможное число ступеней, что влечет за собой рекордную компактность и минимальную массу;
  • Малый уровень шума и малый момент инерции;
  • Распределение нагрузки внутри редуктора позволяет добиться высокой износоустойчивости и способности выдерживать пятикратные перегрузки по сравнению с номинальным крутящим моментом.

 

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

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

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

Элементы цевочного редуктора, работают на сжатие, что обуславливает существенно более высокий запас прочности. Кроме того, конфигурация циклоидальных дисков и внутренней поверхности стационарного зубчатого венца обеспечивает в любой момент времени одновременный контакт до 66% зубьев. Этот факт обуславливает высокую устойчивость к ударным перегрузкам, достигающим 500% от номинального крутящего момента.  
В сравнении с широко распространенными низколюфтовыми редукторами, в редукторах Nabtesco люфт существенно снижен благодаря нашей схеме передачи крутящего момента, поэтому то, что предлагаем мы – это безлюфтовый редуктор.

 

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

 

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

Устройство и принцип действия двухступенчатого планетарного редуктора | Полезные статьи

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

Планетарный редуктор устройство

 

• корпус редуктора, изготовленный из чугуна;

• венец второй ступени, выполненный из стали. Устанавливается внутрь корпуса редуктора;

• водило второй ступени;

• сателлиты; 

• подшипники, устанавливающиеся в сателлиты, которые монтируются в собранном виде в корпус водила второй ступени;

• оси сателлитов;

• верхние стопорные кольца, устанавливаемые на осях. Сателлиты фиксируются осями в корпусе водила;

• далее на оси сателлитов устанавливаются нижние стопорные кольца;

• подшипник, который закрепляется изнутри задней части корпуса. В нем расположен вал водила второй ступени.

 

На вале водила второй ступени закрепляются следующие детали:

 

• дистанционная втулка;

• подшипник;

• стопорное кольцо;

• упорное кольцо;

• сальник;

• прокладка;

• крышка сквозная, фиксируемая болтами.

 

Также в редукторе присутствуют:

 

• отверстие для заливки масла, закрывающееся пробкой;

• подшипник центральной шестерни;

• центральная шестерня, устанавливаемая между сателлитами и хвостовиком внутрь центрального подшипника;

• опорный подшипник водила, расположенный на посадочном месте корпуса водила второй ступени;

• венец первой ступени, который находится в корпусе редуктора на опорном подшипнике водила второй ступени;

• корпус водила первой ступени;

• сателлиты;

• подшипники, устанавливаемые внутрь сателлитов. Сами сателлиты в сборе располагаются в корпусе водила первой ступени;

• оси сателлитов;

• установленные на осях верхние стопорные кольца и сателлиты, зафиксированные осями в корпусе водила;

• нижние стопорные кольца на оси сателлитов.

• подшипник опоры водила первой ступени, расположенный на посадочном месте корпуса водила. 

 

Собранное водило первой ступени монтируем на центральную шестерню опорным подшипником вовнутрь.

 

• второй опорный подшипник на посадочном месте;

• прокладка, закрепленная  на корпусе;

• передний щит, фиксируемый болтами к корпусу.

 

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

 

Планетарный редуктор принцип работы 

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

 

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

 

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

 

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

Принцип работы газового редуктора на авто

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

Как работает газовый редуктор, почему так важно поддерживать его в идеальном рабочем состоянии? Ответы на эти вопросы помогут автовладельцам ориентироваться в выборе данного устройства и вовремя распознавать неполадки ГБО.

Что такое газовый редуктор?

Принцип работы газового редуктора в бытовых баллонах с газом, в магистральной сети газопровода, где требуется корректировка давления, в ГБО автомобилей, аналогичный. Этот прибор предназначен для снижения давления газового топлива и поддержания этого давления на оптимальном уровне.

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

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

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

На заметку! В инновационных комплектах ГБО 5-й и 6-й генераций установка редуктора не требуется: впрыск газа происходит в жидком состоянии.

По какому принципу работает прибор?

Температура тосола должна подняться до 40. Работа газового редуктора возможна только после хорошего прогрева машины. Как работает газовый редуктор на авто?

Происходит следующее:

  1. Жидкий газ из резервуара поступает в фильтр и очищается. Там он и находится, пока электромагнитный клапан в закрытом виде.
  2. Затем горючее проходит через седло клапана 1-й ступени и превращается в пар. Мембрана под его давлением оттягивает коромысло клапана, он опускается на седло и поток газа останавливается. Так получают рабочее давление 0,4 атм. Корректируется оно пружинным механизмом.
  3. Автомобильное газовое топливо продвигается дальше, на седло клапана 2-й ступени. Затем через выпускной штуцер топливо идёт к мотору.

На холостом ходу

Подача топлива во 2-ю ступень регулируется нагрузкой клапана. Вращение регулировочного винта идёт через пружинный механизм. Эластичная мембрана «застывает» в равновесии. В данном случае разрежение от ДВС на холостом ходу компенсируется нагрузкой пружинного механизма. Это и есть принцип работы редуктора ГБО на холостых оборотах.

Что происходит после открытия заслонки?

Дроссельная заслонка открывается и происходит процесс эжекции. Мембранная пластина 2-й ступени отклоняется пропорционально нагрузке, которую испытывает мотор. Она тянет коромысло клапана, открывая седло и увеличивая интенсивность подачи горючего. Такие действия автоматически увеличивают обороты мотора.

Принципы работы редукторов в различных генерациях ГБО

Несмотря на то, что принцип работы редуктора ГБО 1, 2, 3 и 4 поколений подчинён единой задаче, комплектация прибора варьируется в зависимости от поколения. Методы запирания разгрузочного отсека и настройка тоже отличаются.

Редуктор 1-го поколения

В 1-м поколении стоял вакуумный прибор механического типа. Мембранная пластина отзывалась на разрежение во впускном коллекторе, к которому была протянута дополнительная магистраль. Когда мотор заводился, карбюратор начинал втягивать горючее, давление опускалось, вакуумный клапан раскрывал путь горючему. Двигатель глушился, давление приходило в норму и вход топлива блокировался. Регулировался прибор просто: механическим вращением винта жадности. Это основные отличия в принципах действия газового редуктора 1-го и 2-го поколений.

Редуктор 2-го поколения

Принцип работы газового редуктора 2 поколения эволюционировал. Теперь это не вакуумный, электронный прибор. Важное добавление: он оснащён электромагнитным впускным клапаном, управляемым от силового блока. Это обеспечивало автоматическую реакцию прибора на запуск мотора авто.  Электроклапан подаёт газ, ориентируясь на импульсы от датчика кислорода. Это главное достоинство устройства электронного типа, так как у старых карбюраторных двигателей не всегда достаточно вакуума, чтобы работала мембрана.

Редуктор 3-го и 4-го генераций

В 3-й и 4-й генерации ГБО данный прибор по конструкции упрощён. Часть опций передана коллектору. Отпала потребность в большом количестве мембранных перегородок. Для разделённой системы впрыска хватает 2 ступени и одного электроклапана.

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

Механизм настраивать стало удобнее. Электронный блок управления подключается к ноутбуку. Запускается специальная программа, которая проводит настройку и диагностику. Хорошо поддаются регулировке приборы от брендов Томасетто и Ловато.

Принцип работы газового редуктора ГБО нужно учитывать при выборе данного устройства. Обращать внимание, к какой генерации он относится, есть ли на нём пыле- и влагозащита. Мощностные характеристики должны быть с запасом. А вот габариты при этом должны соответствовать площади, которое можно отвести на устройство в подкапотном пространстве.

Цилиндрический редуктор с параллельными валами, особенности работы

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

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

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

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

Наиболее популярны моноблоки с передаваемым крутящим моментом 100 – 1500 Nm и мощностью 3-160 кВт. Выбор цилиндрического редуктора зависит от требуемых показателей передаточного числа – чем выше цифра, тем больше количество ступеней в устанавливаемом редукторе.

Преимущества применения цилиндрических редукторов

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

  • практически отсутствует люфт выходного вала – высокая кинематическая точность дает возможность использовать редуктор в станках, приборах с повышенными технологическими требованиями;
  • эксплуатация оборудования в условиях частых циклов «пуск/остановка» — за счет малых показателей трения скольжения сохраняется прочность рабочих деталей даже при экстремальных нагрузках;
  • компактные размеры обеспечивают монтаж цилиндрического редуктора в условиях ограниченности рабочего пространства;
  • устойчивость к осевому, радиальному «биению».

Где используется цилиндрический редуктор с параллельными валами

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

Основы Redux, часть 3: состояние, действия и редукторы

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

Введение​

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

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

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

Настройка проекта

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

Для начала вы можете открыть и разветвить эту CodeSandbox:

Вы также можете клонировать тот же проект из этого репозитория Github. После клонирования репозитория вы можете установить инструменты для проекта с помощью npm install и запустить его с помощью npm start .

Если вы хотите увидеть окончательную версию того, что мы собираемся построить, вы можете просмотреть tutorial-steps ветку или просмотреть окончательную версию в этой CodeSandbox.

Создание нового проекта Redux + React

После того, как вы закончите это руководство, вы, вероятно, захотите попробовать работать над своими собственными проектами. Мы рекомендуем использовать шаблоны Redux для Create-React-App как самый быстрый способ создать новый проект Redux + React . Он поставляется с уже настроенным Redux Toolkit и React-Redux, используя модернизированную версию примера «встречного» приложения, которое вы видели в части 1. Это позволяет вам сразу перейти к написанию фактического кода приложения без необходимости добавлять пакеты Redux и настраивать магазин.

Если вы хотите узнать подробности о том, как добавить Redux в проект, см. это объяснение:

Подробное объяснение: Добавление Redux в проект React

Шаблон Redux для CRA поставляется с уже настроенным Redux Toolkit и React-Redux. .Если вы настраиваете новый проект с нуля без этого шаблона, выполните следующие действия:

  • Добавьте пакеты @reduxjs/toolkit и react-redux
  • Создайте хранилище Redux с помощью API RTK configureStore , и передайте хотя бы одну функцию редуктора
  • Импортируйте хранилище Redux в файл точки входа вашего приложения (например, src/index.js )
  • Оберните ваш корневой компонент React компонентом из React-Redux , например:
  ReactDOM.render( 


,
document.getElementById('root')
)
Копировать
Изучение исходного проекта

Этот исходный проект основан на стандартном шаблоне проекта Create-React-App с некоторыми изменениями.

Давайте кратко рассмотрим, что содержит исходный проект:

  • /src
    • index.js : файл точки входа для приложения.Он отображает основной компонент .
    • App.js : основной компонент приложения.
    • index.css : стили для всего приложения поддельный REST API для наших данных. Наше приложение получит данные из этих поддельных конечных точек позже.
  • /exampleAddons : содержит некоторые дополнительные дополнения Redux, которые мы будем использовать позже в руководстве, чтобы показать, как все работает в противном случае приложение пусто.

    Итак, приступим!

    Запуск примера приложения Todo

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

    Определение требований

    Начнем с определения первоначальных бизнес-требований для этого приложения:

    • Пользовательский интерфейс должен состоять из трех основных разделов:
      • Поле ввода, позволяющее пользователю вводить текст нового элемента списка дел.
      • Список всех существующих задач
      • Нижний колонтитул, показывающий количество незавершенных задач и параметры фильтрации
    • Элементы списка задач должны иметь флажок, который переключает их статус «завершено».Мы также должны иметь возможность добавить цветовую кодировку тег категории для предопределенного списка цветов и удаления элементов списка дел.
    • Счетчик должен иметь множественное число активных задач: «0 элементов», «1 элемент», «3 элемента» и т. д.
    • Должно быть два способа фильтрации отображаемых задач в списке:
      • Фильтрация на основе отображения задач «Все», «Активные» и «Завершенные»
      • Фильтрация на основе выбора одного или нескольких цветов и отображения любых задач чей тег соответствует этим цветам

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

    Конечная цель — это приложение, которое должно выглядеть так:

    Проектирование значений состояния

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

    Концептуально существует два основных аспекта этого приложения:

    • Фактический список текущих задач
    • Текущие параметры фильтрации

    Нам также необходимо отслеживать данные, которые пользователь вводит в » Поле ввода «Добавить задачу», но это менее важно и мы займемся этим позже.

    Для каждого элемента списка дел нам нужно хранить несколько частей информации:

    • Текст, введенный пользователем
    • Логический флаг, говорящий о том, выполнено задание или нет
    • Уникальное значение идентификатора
    • Цветовая категория, если она выбрана

    Наше фильтрующее поведение, вероятно, можно описать с помощью некоторых перечисленных значений:

    • Состояние завершения: «Все», «Активно» и «Завершено»
    • Цвета: «Красный», «Желтый», «Зеленый», » Синий», «Оранжевый», «Фиолетовый»

    Глядя на эти значения, мы также можем сказать, что задачи — это «состояние приложения» (основные данные, с которыми работает приложение), в то время как значения фильтрации — «состояние пользовательского интерфейса» (состояние, которое описывает, что приложение делает прямо сейчас).Это может быть полезно подумайте об этих разных категориях, чтобы понять, как используются разные части состояния.

    Проектирование структуры состояния

    В Redux состояние нашего приложения всегда хранится в простых объектах и ​​массивах JavaScript . Это означает, что вы не можете поставить другие вещи в состояние Redux — никаких экземпляров классов, встроенных типов JS, таких как Map / Set Promise / Date , функций или чего-либо еще, что не является простыми данными JS.

    Значение состояния корневого Redux почти всегда представляет собой простой объект JS с другими данными, вложенными внутрь него.

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

    • Во-первых, нам нужен массив объектов элементов задач. Каждый элемент должен иметь эти поля:
      • ID : Уникальный номер
      • Text : Текст Пользователь, набранный в
      • Завершен : Логический флаг
      • Цвет : Дополнительный цвет категории
    • Затем нам нужно описать наши параметры фильтрации.Нам нужно:
      • Текущее «завершенное» значение фильтра
      • Массив выбранных на данный момент цветовых категорий

    Итак, вот как может выглядеть пример состояния нашего приложения:

      const todoAppState = { 
    todos: [
    { id: 0, текст: 'Learn React', завершено: true},
    { id: 1, текст: 'Learn Redux', завершено: false, цвет: 'фиолетовый'},
    { id: 2, текст: «Построй что-нибудь интересное!», завершено: ложь, цвет: «синий» }
    ],
    фильтров: {
    статус: «Активно»,
    цвета: ['красный', 'синий']
    }
    }
    Копировать

    Важно отметить, что допустимо иметь другие значения состояния вне Redux! .Этот пример пока достаточно мал, так что у нас действительно есть все наше состояние в хранилище Redux, но, как мы увидим позже, некоторые данные действительно не нужно хранить в Redux (например, «это раскрывающееся меню открыто?» или «текущее значение ввода формы»).

    Разработка действий

    Действия — это простые объекты JavaScript, которые имеют поле типа . Как упоминалось ранее, вы можете думать о действии как о событии, описывающем что-то, что произошло в приложении .

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

    • добавить новую запись задачи на основе текста, введенного пользователем
    • переключить состояние выполнения задачи
    • выбрать цветовую категорию для задачи
    • Удалить задачу
    • Пометить все задачи как завершенные
    • Удалить все завершенные задачи
    • Выбрать другое значение фильтра «Завершено»
    • Добавить новый цветовой фильтр
    • Удалить цветовой фильтр опишите, что происходит в действии .поле полезной нагрузки . Это может быть число, строка или объект с несколькими полями внутри.

      Магазину Redux все равно, какой фактический текст поля action.type . Однако ваш собственный код будет выглядеть по адресу action.type , чтобы узнать, требуется ли обновление. Кроме того, вы будете часто просматривать строки типа действия в Redux. Расширение DevTools во время отладки, чтобы увидеть, что происходит в вашем приложении. Итак, старайтесь выбирать типы действий, которые читабельно и ясно описывайте происходящее — вам будет намного легче понять вещи, когда вы посмотрите на них позже!

      На основе этого списка вещей, которые могут произойти, мы можем создать список действий, которые будет использовать наше приложение:

      • {тип: 'todos/todoAdded', полезная нагрузка: todoText}
      • {тип: ' todos/todoToggled', полезная нагрузка: todoId}
      • {тип: 'todos/colorSelected, полезная нагрузка: {todoId, цвет}}
      • {тип: 'todos/todoDeleted', полезная нагрузка: todoId}

        {type: ‘todos/allCompleted’}

      • {type: 'todos/completedCleared'}
      • {type: 'filters/statusFilterChanged', payload: filterValue}
      • 9002filterChangerfiltertype ‘, payload: {color, changeType}}

      В этом случае действия в основном имеют один дополнительный фрагмент данных, поэтому мы можем поместить его непосредственно в действие .поле полезной нагрузки . Мы могли бы разделить поведение цветового фильтра на два действия: одно для «добавлено» и одно для «удалено», но в этом случае мы сделаем это как одно действие с дополнительным полем внутри специально, чтобы показать, что мы можем иметь объекты в качестве полезной нагрузки действия.

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

      Написание редьюсеров

      Теперь, когда мы знаем, как выглядит структура нашего состояния и наши действия, пришло время написать наш первый редьюсер.

      Редьюсеры — это функции, которые принимают текущее состояние и действие в качестве аргументов и возвращают новый результат состояния . Другими словами, (состояние, действие) => newState .

      Создание корневого редуктора

      Приложение Redux действительно имеет только одну функцию редуктора: функцию «корневого редуктора» , которую вы передадите в createStore позже. Эта одна корневая редукторная функция отвечает за обработку всех отправленных действий и вычисление того, каким должен быть весь результат нового состояния каждый раз.

      Начнем с создания файла reducer.js в папке src вместе с index.js и App.js .

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

      src/reducer.js

        const initialState = { 
      todos: [
      { id: 0, text: 'Learn React', Completed: true} ,
      {идентификатор: 1, текст: «Изучите Redux», завершено: false, цвет: «фиолетовый»},
      {id: 2, текст: «Создайте что-нибудь веселое!», завершено: ложь, цвет: «синий»}
      ],
      фильтры: {
      статус: 'Все',
      цвета: []
      }
      }


      функция экспорта по умолчанию appReducer(state = initialState, action) {

      switch (action.type) {

      по умолчанию:


      return state
      }
      }
      Copy

      Редуктор может быть вызван с undefined в качестве значения состояния при инициализации приложения. Если это произойдет, нам нужно предоставить начальное значение состояния, чтобы остальной части кода редуктора было с чем работать. Редукторы обычно используют синтаксис аргумента ES6 по умолчанию для предоставления начального состояния: (состояние = initialState, действие) .

      Далее добавим логику для обработки действия 'todos/todoAdded' .

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

      src/reducer.js

        function nextTodoId(todos) { 
      const maxId = todos.reduce((maxId, todo) => Math.max(todo.id, maxId), -1)
      return maxId + 1
      }


      функция экспорта по умолчанию appReducer(state = initialState, action) {

      switch (action.type) {

      case 'todos/todoAdded': {

      return {

      ...state,

      todos: [

      ...state.todos,

      {

      id: next.todoId ),
      текст: action.payload,
      завершено: false
      }
      ]
      }
      }
      по умолчанию:


      состояние возврата
      }
      }
      Копия 90.12 Это

      .. ужасно много работы, чтобы добавить один элемент списка дел в состояние. Зачем нужна вся эта дополнительная работа?

      Правила редукторов

      Ранее мы говорили, что редукторы должны всегда следовать некоторым специальным правилам :

      • Они должны вычислять новое значение состояния только на основе состояния и действия аргументов :

        • не разрешено изменять существующее состояние . Вместо этого они должны сделать неизменяемые обновления , скопировав существующее состояние и внеся изменения в скопированные значения.
        • Они не должны использовать асинхронную логику или другие «побочные эффекты»

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

        • Запись значения в консоль
        • Сохранение файла
        • Установка асинхронного таймера
        • Выполнение HTTP-запроса AJAX
        • Изменение некоторого состояния, существующего вне функции, или преобразование аргументов в функцию
        • Создание случайных чисел или уникальных случайных идентификаторов (например, Math.random() или Date.now() )

        Любая функция, которая следует этим правилам, также известна как «чистая» функция , даже если она специально не написана как функция-редуктор.

        Но чем важны эти правила? Есть несколько разных причин:

        • Одна из целей Redux — сделать ваш код предсказуемым. Когда выходные данные функции вычисляются только из входных аргументов, легче понять, как работает этот код, и протестировать его.
        • С другой стороны, если функция зависит от внешних переменных или ведет себя случайным образом, вы никогда не знаете, что произойдет, когда вы ее запустите.
        • Если функция изменяет другие значения, включая свои аргументы, это может неожиданно изменить способ работы приложения. Это может быть распространенным источником ошибок, таких как «Я обновил свое состояние, но теперь мой пользовательский интерфейс не обновляется, когда должен!»
        • Некоторые возможности Redux DevTools зависят от того, правильно ли ваши редьюсеры следуют этим правилам.

          Редукторы и неизменяемые обновления

          Ранее мы говорили о «мутации» (изменение существующих значений объекта/массива) и «неизменяемости» (обработка значений как чего-то, что нельзя изменить).

          В Redux наши редукторы никогда не могут изменять исходные / текущие значения состояния!

          Существует несколько причин, по которым вы не должны изменять состояние в Redux:

          • Это вызывает ошибки, например, пользовательский интерфейс не обновляется должным образом, чтобы отображать последние значения
          • Это затрудняет понимание того, почему и как было изменено состояние обновлено
          • Это затрудняет написание тестов
          • Это лишает возможности правильно использовать «отладку с перемещением во времени»
          • Это противоречит предполагаемому духу и шаблонам использования Redux

          Итак, если мы не можем изменить оригиналы , как нам вернуть обновленное состояние?

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

            
          return {
          ...state,
          value: 123
          }
          Copy

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

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

          Однако, если вы думаете, что «написание неизменяемых обновлений вручную таким способом сложно запомнить и сделать правильно»… Да, ты прав! 🙂

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

          В реальных приложениях вам не придется писать эти сложные вложенные неизменяемые обновления вручную . В части 8: Modern Redux с Redux Toolkit вы узнаете узнайте, как использовать Redux Toolkit, чтобы упростить написание неизменяемой логики обновления в редьюсерах.

          Обработка дополнительных действий

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

          src/reducer.js

            функция экспорта по умолчанию appReducer(state = initialState, action) { 
          switch (action.type) {
          case 'todos/todoAdded ': {
          return {
          ...state,
          todos: [
          ...state.todos,
          {
          id: nextTodoId(state.todos),
          текст: action.payload,
          выполнено: false
          }
          ]
          }
          }
          case 'todos/todoToggled': {
          return {

          ...state,

          todos: state.todos.map(todo => {

          if (todo.id !== action.payload) {
          return todo
          }


          return {
          ...todo,

          завершено: !todo.completed
          }
          })
          }
          }
          по умолчанию:
          состояние возврата
          }
          }
          Копировать

          И так как мы сосредоточились на состоянии todos, давайте добавим случай также действие «выбор видимости изменено»:

          src/reducer.js

            функция экспорта по умолчанию appReducer(state = initialState, action) { 
          switch (action.type) {
          case 'todos/todoAdded': {
          return {
          ...state,
          todos: [
          ... state.todos,
          {
          id: nextTodoId(state.todos),
          текст: action.payload,
          завершено: false
          }
          ]
          }
          }
          case 'todos/todoToggled': {
          return {
          ...state,
          todos: state.todos.map(todo => {
          if (todo.id !== action.payload) {
          return todo
          }

          return {
          ...todo,
          завершено: ! todo.completed
          }
          })
          }
          }
          case 'filters/statusFilterChanged': {
          return {

          ...состояние,

          фильтры: {

          ...state.filters,

          status: action.payload
          }
          }
          }
          default:
          return state
          }
          }
          Копировать

          Мы обработали только 3 действия, но это уже становится слишком долго . Если мы попытаемся обработать каждое действие в этом редукторе функция, будет трудно прочитать все это.

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

          Разделение редукторов

          В рамках этого редукторы Redux обычно разделяются на части в зависимости от раздела состояния Redux, который они обновляют . Наше состояние приложения todo в настоящее время имеет два раздела верхнего уровня: state.todos и state.filters . Итак, мы можем разделить функцию большого корневого редуктора на два меньших редуктора — todosReducer и filterReducer .

          Итак, где же должны жить эти разделяющие редюсерные функции?

          Мы рекомендуем организовывать папки и файлы вашего приложения Redux на основе «функций» — кода, относящегося к определенной концепции. или области вашего приложения. Код Redux для конкретной функции обычно записывается в виде одного файла, известного как файл «slice» , который содержит всю логику редуктора и весь код, связанный с действиями, для этой части состояния вашего приложения.

          Из-за этого редюсер для определенного раздела состояния приложения Redux называется «редуктором среза» . Как правило, некоторые из объектов действия будут тесно связаны с определенным редюсером фрагмента, поэтому строки типа действия должны начинаться с имени этой функции (например, 'todos' ) и описывать произошедшее событие (например, 'todoAdded ' ), объединенные в одну строку ( 'todos/todoAdded' ).

          В нашем проекте создайте новую папку features , а затем внутри нее папку todos . Создайте новый файл с именем todosSlice.js и давайте вырежем и вставим исходное состояние, связанное с задачей, в этот файл:

          src/features/todos/todosSlice.js

            const initialState = [ 
          { id: 0 , текст: «Изучите React», завершено: true},
          {id: 1, текст: «Изучите Redux», завершено: false, цвет: «фиолетовый»},
          {id: 2, текст: «Создайте что-нибудь веселое! ', завершено: false, цвет: 'синий' }
          ]

          function nextTodoId(todos) {
          const maxId = todos.reduce((maxId, todo) => Math.max(todo.id, maxId), -1)
          return maxId + 1
          }

          экспортировать функцию по умолчанию todosReducer(state = initialState, action) {
          switch (action.type ) {
          по умолчанию:
          состояние возврата
          }
          }
          Копировать

          Теперь мы можем скопировать логику обновления todos. Однако здесь есть важное отличие. Этот файл должен только обновить состояние, связанное с todos — он больше не вложен! Это еще одна причина, по которой мы разделили редукторы.Поскольку состояние todos само по себе является массивом, нам не нужно копировать сюда внешний объект корневого состояния. Это делает этот редуктор более легким для чтения.

          Это называется редукторной композицией и является фундаментальным шаблоном создания приложений Redux.

          Вот как выглядит обновленный редюсер после выполнения этих действий:

          src/features/todos/todosSlice.js

            экспортировать функцию по умолчанию todosReducer(state = initialState, action) { 
          switch (action.type) {
          case 'todos/todoAdded': {

          return [
          ...state,
          {
          id: nextTodoId(state),
          text: action.payload,
          выполнено: false
          }
          ]
          }
          case 'todos/todoToggled': {
          return state.map(todo => {
          if (todo.id !== action.payload) {
          return todo
          }

          return {
          ...todo,
          выполнено: !сделать.завершено
          }
          })
          }
          по умолчанию:
          состояние возврата
          }
          }
          Копировать

          Это немного короче и удобнее для чтения.

          Теперь мы можем сделать то же самое для логики видимости. Создайте src/features/filters/filtersSlice.js и переместите туда весь код, связанный с фильтрами:
          цвета: []
          }

          функция экспорта по умолчанию filterReducer(state = initialState, action) {
          switch (action.type) {
          case ‘filters/statusFilterChanged’: {
          return {

          …state,
          status: action.payload
          }
          }
          default:
          return state
          }
          }
          1 чтобы скопировать объект, содержащий состояние фильтров, но поскольку вложенности меньше, то проще читать, что происходит.

          Объединение редукторов

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

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

          src/reducer.js

            импортировать todosReducer из './features/todos/todosSlice' 
          импортировать filterReducer из './features/filters/filtersSlice'

          функция экспорта по умолчанию rootReducer(state = {}, action) {

          return {

          todos: todosReducer(state.todos, action),

          фильтры: filterReducer(state.filters, action) )
          }
          }
          Копировать

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

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

          combReducers

          Мы видим, что новый корневой редьюсер делает одно и то же для каждого слайса: вызывает слайс-редьюсер, передает в срез состояние, принадлежащее этому редюсеру, и присваивает результат обратно корню. государственный объект. Если бы мы добавили больше фрагментов, шаблон повторил бы.

          Базовая библиотека Redux включает утилиту combReducers , которая выполняет тот же шаблонный шаг за нас. Мы можем заменить наш написанный от руки rootReducer на более короткий, сгенерированный combReducers .

          Теперь, когда нам нужно CombineReducers , пришло время установить основную библиотеку Redux :

          После этого мы можем импортировать combReducers и использовать его:

          src/reducer.13 9002 combReducers } from ‘redux’

          import todosReducer from ‘./features/todos/todosSlice’
          import filterReducer from ‘./features/filters/filtersSlice’

          const rootReducer = combReducers({

          фильтры: todos0Reducer filterReducer
          })

          export default rootReducer
          Copy

          combReducers принимает объект, в котором имена ключей станут ключами в вашем объекте корневого состояния, а values ​​— это функции редуктора слайсов, которые знают, как обновлять эти слайсы состояния Redux.

          Помните, имена ключей, которые вы даете combReducers , решают, какими будут имена ключей вашего объекта состояния!

          Чему вы научились

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

          Вот содержимое нашего приложения на данный момент:

          • Приложения Redux используют простые объекты JS, массивы и примитивы в качестве значений состояния
            • Значение корневого состояния должно быть простым объектом JS
            • Состояние должно содержать наименьший объем данных, необходимый для работы приложения
            • Классы, промисы, функции и другие непростые значения должны не переходить в состояние Redux
            • Редукторы не должны создавать случайные значения, такие как Math.random() или Date.now()
            • Можно иметь другие значения состояния, которых нет в хранилище Redux (например, состояние локального компонента) рядом с Redux
          • Действия — это простые объекты с поле типа , которое описывает, что произошло
            • Поле типа должно быть читаемой строкой и обычно записывается как 'feature/eventName'
            • Действия могут содержать другие значения, которые обычно хранятся в действие.поле полезной нагрузки
            • Действия должны иметь наименьшее количество данных, необходимых для описания того, что произошло
          • Редьюсеры — это функции, которые выглядят как (состояние, действие) => newState
            • Редьюсеры всегда должны следовать специальным правилам:
              • Вычислять только новое состояние на основе состояния и аргументов действия
              • Никогда не изменять существующее состояние — всегда возвращать копию Редюсеры должны быть разделены, чтобы их было легче читать
                • Редюсеры обычно разделяются на основе ключей состояния верхнего уровня или «фрагментов» состояния
                • Редюсеры обычно записываются в файлы «фрагментов», организованные в папки «функций»
                • Редьюсеры можно комбинировать вместе с функцией Redux combReducers определить ключи объекта состояния верхнего уровня

              Что дальше?Им нужно для помещения в хранилище Redux, которое может вызывать код редуктора с действиями, когда что-то произошло.

              В части 4: Store мы увидим, как создать хранилище Redux и запустить логику нашего редуктора.

              Что такое Reducer в JavaScript/React/Redux

              Концепция Reducer стала популярной в JavaScript с появлением Redux как решения для управления состоянием для React. Но не беспокойтесь, вам не нужно изучать Redux, чтобы понимать редукторы. В основном редьюсеры предназначены для управления состоянием в приложении.Например, если пользователь что-то пишет в поле ввода HTML, приложение должно управлять этим состоянием пользовательского интерфейса (например, управляемыми компонентами).

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

               
               

              (состояние, действие) => newState

              Например, в JavaScript для сценария увеличения числа на единицу это будет выглядеть следующим образом:

               
               

              функция counterReducer(состояние, действие) {

              состояние возврата + 1;

              }

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

               
               

              const counterReducer = (state, action) => {

              return state + 1;

              };

              В этом случае текущее состояние является целым числом (т.г. count), а функция редуктора увеличивает счетчик на единицу. Если бы мы переименовали аргумент state в count , он мог бы стать более читабельным и доступным для новичков в этой концепции. Однако имейте в виду, что count по-прежнему является состоянием:

               
               

              const counterReducer = (count, action) => {

              return count + 1;

              };

              Функция редьюсера — это чистая функция без каких-либо побочных эффектов, что означает, что при одних и тех же входных данных (т.г. состояние и действие ), ожидаемый результат (например, newState ) всегда будет одним и тем же. Это делает функции-редьюсеры идеально подходящими для рассуждений об изменениях состояния и их изолированного тестирования. Вы можете повторить тот же тест с теми же входными данными в качестве аргументов и всегда ожидать одного и того же результата:

               
               

              expect(counterReducer(0)).to.equal(1);

              ожидать(counterReducer(0)).to.equal(1);

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

               
               

              const counterReducer = (count, action) => {

              if (action.type === 'INCREASE') {

              return count + 1;

              }

              if (action.type === 'УМЕНЬШЕНИЕ') {

              счетчик возврата - 1;

              }

              количество возвратов;

              };

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

               
               

              expect(counterReducer(0, { тип: 'УВЕЛИЧЕНИЕ' })).to.equal(1);

              ожидать (counterReducer (0, { тип: 'УВЕЛИЧЕНИЕ' })).to.equal (1);

              ожидать (counterReducer (0, { тип: 'УМЕНЬШЕНИЕ' })).to.equal (-1);

              ожидать (counterReducer (0, { тип: 'UNMATCHING_ACTION' })).равно (0);

              Однако более вероятно, что вы увидите оператор switch case вместо операторов if else, чтобы отобразить несколько переходов состояний для функции-редюсера. Следующий редуктор выполняет ту же логику, что и раньше, но выражен оператором case case:

               
               

              const counterReducer = (count, action) => {

              switch (action.type) {

              case 'INCREASE':

              количество возвратов + 1;

              case 'УМЕНЬШЕНИЕ':

              количество возвратов - 1;

              по умолчанию:

              количество возвратов;

              }

              };

              В этом сценарии количество само по себе является состоянием, к которому мы применяем наши изменения состояния, увеличивая или уменьшая количество.Однако часто у вас будет не примитив JavaScript (например, целое число для подсчета) в качестве состояния, а сложный объект JavaScript. Например, count может быть одним из свойств нашего объекта состояния :

               
               

              const counterReducer = (state, action) => {

              switch (action.type) {

              case 'INCREASE':

              return {...состояние, счет: состояние.счет + 1};

              case 'УМЕНЬШЕНИЕ':

              return { ...состояние, количество: состояние.количество - 1 };

              по умолчанию:

              состояние возврата;

              }

              };

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

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

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

              Давайте рассмотрим эти две важные точки в коде на другом примере, где мы хотим изменить фамилию объекта человека с помощью следующей функции редюсера:

               
               

              const personReducer = (person, action) => {

              switch (action.type) {

              case 'INCREASE_AGE':

              return { ...человек, возраст: person.age + 1 };

              case 'CHANGE_LASTNAME':

              return { ...человек, фамилия: action.lastname };

              по умолчанию:

              вернуть человека;

              }

              };

              В тестовой среде мы могли бы изменить фамилию пользователя следующим образом: ,

              };

              const action = {

              тип: 'CHANGE_LASTNAME',

              фамилия: 'Верух',

              };

              const result = personReducer(initialState, action);

              ожидать(результат).to.equal({

              имя: 'Лиза',

              фамилия: 'Верух',

              возраст: 30,

              });

              Вы видели, что с помощью оператора распространения JavaScript в нашей функции редьюсера мы используем все свойства объекта текущего состояния для нового объекта состояния, но переопределяем определенные свойства (например, фамилия ) для этого нового объекта. Вот почему вы часто будете видеть оператор распространения для сохранения неизменности операции состояния (= состояние не изменяется напрямую).

              Также вы видели еще один аспект функции редуктора: Действие, предусмотренное для функции редуктора, может иметь необязательную полезную нагрузку (например, фамилия ) рядом со свойством обязательного типа действия. Полезная нагрузка — это дополнительная информация для выполнения перехода между состояниями. Например, в нашем примере редьюсер не знал бы новую фамилию нашего человека без дополнительной информации.

              Часто необязательная полезная нагрузка действия помещается в другое универсальное свойство полезной нагрузки , чтобы сохранить свойства верхнего уровня объекта действия в более общем виде (.например {тип, полезная нагрузка} ). Это полезно для того, чтобы тип и полезная нагрузка всегда были разделены рядом. Для нашего предыдущего примера кода это изменит действие на следующее:

               
               

              const action = {

              type: 'CHANGE_LASTNAME',

              payload: {

              lastname: 'Wieruch',

              },

              };

              Функция редуктора тоже должна измениться, потому что она должна погружаться на один уровень глубже в действие:

               
               

              const personReducer = (person, action) => {

              switch (action.type) {

              case 'INCREASE_AGE':

              return { ...человек, возраст: person.age + 1 };

              case 'CHANGE_LASTNAME':

              return { ...человек, фамилия: action.payload.lastname };

              по умолчанию:

              вернуть человека;

              }

              };

              По сути, вы узнали все, что вам нужно знать о редукторах. Они используются для выполнения переходов состояний из A в B с помощью действий, предоставляющих дополнительную информацию.Вы можете найти примеры редукторов из этого руководства в этом репозитории GitHub, включая тесты. Здесь снова все в двух словах:

              • Синтаксис: По сути функция редьюсера выражается как (состояние, действие) => newState .
              • Неизменяемость: Состояние никогда не изменяется напрямую. Вместо этого редьюсер всегда создает новое состояние.
              • Переходы состояний: Редюсер может иметь условные переходы состояний.
              • Действие: Общий объект действия поставляется с обязательным свойством типа и дополнительными полезными данными:
                • Свойство типа выбирает условный переход состояния.
                • Полезная нагрузка действия предоставляет информацию для перехода состояния.

              Также ознакомьтесь с этим руководством, если хотите узнать, как использовать редюсеры в React с хуком useReducer.

              Редукторы в JavaScript. Полное введение в JS-редукторы… | Надун Малинда

              Редуктор — это не что иное, как простая функция, которая принимает два аргумента и на основе этих двух аргументов возвращает новое значение состояния.

              Редуктор примет состояние и действие, а затем вернет новое состояние.Bellow — это псевдофункция, представляющая эту концепцию.

              Это можно переписать следующим образом с помощью функции стрелки JavaScript.

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

              На основе свойства type в объекте action редьюсер может условно возвращать новое состояние.

              Это простое использование концепции редюсера, но чаще всего в ваших приложениях будут сложные состояния. Очень часто внутри функций-редюсеров используется оператор switch-case вместо оператора if-else .Если это переписать с использованием switch-case, будет выглядеть так:-

              Тогда давайте посмотрим на немного сложную функцию редуктора с дополнительным свойством (объектом) полезной нагрузки в объекте действия .

              Прежде чем углубиться в саму функцию редуктора, мне нужно выделить несколько моментов в приведенном выше разделе кода. Во-первых, в строке номер 2 я использовал уничтожение объекта JavaScript, чтобы извлечь свойства из объекта полезной нагрузки и привязать их к константе. Если вы не знакомы с этой концепцией, я рекомендую сначала пройтись по ней.

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

              Обратите внимание, что в строках 8 и 17 выше я использую оператор распространения JavaScript, чтобы изменить только часть совпадающей задачи, не касаясь остальной части этой задачи.А также, используя метод карты массива , я всегда гарантирую возврат нового массива , используя входящий массив todos . Так мы обеспечиваем неизменность состояния, в нашем случае todos.

              Итак, мы можем использовать нашу функцию todoReducer в нашем приложении, как показано ниже. Пожалуйста, обратите внимание на то, как я определяю объект действия и его необязательное свойство полезной нагрузки. Свойство полезной нагрузки (объект) состоит из минимального количества информации, которую наши функции редуктора должны знать для изменения состояния.

              После вызова нашего todoReducer новый объект состояния todos будет выглядеть следующим образом:

              с которыми приходится сталкиваться при разработке веб-сайтов React.

              useState — это, конечно, наиболее распространенный способ создания и управления состоянием в (функциональных) компонентах React.

              Также существует множество библиотек, предлагающих самоуверенные способы управления всем вашим состоянием (или его частью), например Redux, Mobx, Recoil или XState.

              Но прежде чем перейти к библиотеке, которая поможет вам управлять проблемами с состоянием, вы должны знать о другом родном способе управления своим состоянием в React: useReducer . Он может быть очень мощным, если его использовать правильно и для правильной цели. На самом деле, он настолько мощный, что знаменитую библиотеку Redux можно рассматривать как просто большой оптимизированный useReducer (как мы увидим).

              В этой статье мы начнем с объяснения того, что такое useReducer и как его использовать, предоставив вам хорошую ментальную модель и примеры.Затем мы рассмотрим сравнение useState и useReducer , чтобы узнать, когда что использовать.

              А для пользователей TypeScript мы также увидим, как использовать TypeScript и useReducer вместе.

              Давайте погрузимся! Что такое React? Вы, вероятно, уже хорошо знакомы с первым, поэтому полезно начать с него, чтобы понять useReducer .

              useState и useReducer : быстрое сравнение

              На первый взгляд они очень похожи. Давайте посмотрим на них рядом:

                const [state, setState] = useState(initialValue);
              
              const [состояние, отправка] = useReducer (редуктор, начальное значение);
                

              Как видите, в обоих случаях хук возвращает массив из двух элементов. Первый — это состояние , а второй — функция, позволяющая изменять состояние: setState для useState и dispatch для useReducer .О том, как работает диспетчер , мы узнаем позже.

              Исходное состояние предоставляется как для useState , так и для useReducer . Основное различие в аргументах хука заключается в редюсере , предоставляемом useReducer .

              А пока я просто скажу, что этот редуктор — это функция, которая будет обрабатывать логику того, как должно обновляться состояние. Мы также узнаем об этом подробно позже в статье.

              Теперь давайте посмотрим, как изменить состояние с помощью setState или dispatch .Для этого воспользуемся проверенным примером счетчика — мы хотим увеличивать его на единицу при нажатии кнопки:

               
              

              Хотя версия useState , вероятно, вам знакома (если нет, то это может быть потому, что мы используем форму функционального обновления setState ), версия useReducer может выглядеть немного странно.

              Почему мы передаем объект со свойствами type и payload ? Откуда берется (магическое?) значение «приращение» ? Не волнуйтесь, тайны будут объяснены!

              На данный момент вы можете заметить, что обе версии очень похожи. В любом случае вы обновляете состояние, вызывая функцию обновления ( setState или dispatch ) с информацией о том, как именно вы хотите обновить состояние.

              Давайте теперь рассмотрим на высоком уровне, как именно работает версия useReducer .

              useReducer : внутренняя ментальная модель

              В этом разделе я хочу дать вам хорошую ментальную модель того, как работает хук useReducer . Это важно, потому что, когда мы по колено в деталях реализации, все может стать немного ошеломляющим. Особенно, если вы никогда раньше не работали с подобными структурами.

              Один из способов думать о useReducer — думать о нем как о бэкенде. Это может показаться немного странным, но потерпите меня: мне очень нравится эта аналогия, и я думаю, что она хорошо объясняет редукторы.

              Серверная часть обычно структурирована с определенным способом сохранения данных (база данных) и API, позволяющим изменять базу данных.

              Этот API имеет конечные точки HTTP, которые вы можете вызывать. Запросы GET позволяют получить доступ к данным, а запросы POST позволяют изменять их. Когда вы выполняете запрос POST, вы также можете указать некоторые параметры; например, если вы хотите создать нового пользователя, вы обычно включаете имя пользователя, адрес электронной почты и пароль этого нового пользователя в HTTP-запрос POST.

              Итак, чем useReducer похож на бэкенд? Ну:

              • состояние это база данных.Он хранит ваши данные.
              • диспетчеризация эквивалентна оконечным точкам API, вызываемым для изменения базы данных.
                • Вы можете выбрать конечную точку для вызова, указав тип вызова.
                • Дополнительные данные можно указать с помощью свойства payload , которое соответствует телу запроса POST.
                • И тип , и полезная нагрузка являются свойствами объекта, который передается редуктору .Этот объект называется действием .
              • редюсер это логика API. Он вызывается, когда серверная часть получает вызов API (вызов диспетчеризации ) и обрабатывает, как обновить базу данных на основе конечной точки и содержимого запроса (действие ).

              Вот полный пример использования useReducer . Уделите немного времени тому, чтобы осознать это и сравнить с ментальной моделью, описанной выше.

                импорт {useReducer} из «реагировать»;
              
              
              const InitialState = {количество: 0};
              
              
              
              const reducer = (состояние, действие) => {
                если (действие.тип === 'приращение') {
                  return { count: state.count + action.payload };
                }
              };
              
              приложение функции () {
                
                
                const [состояние, отправка] = useReducer (редуктор, initialState);
              
                возвращение (
                  <дел>
                    {}
                    Количество: {state.count}
                    {}
                    
); } экспортировать приложение по умолчанию;

Вы видите, как они связаны?

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

Теперь, когда (надеюсь) у вас есть хорошее представление о том, как useReducer работает на высоком уровне, давайте углубимся в детали.

Как работает редьюсер

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

Как вы могли заметить из приведенного выше примера, редьюсер — это функция, которая принимает два аргумента.Первое — это текущее состояние , а второе — действие (которое в нашей аналогии с серверной частью соответствует конечной точке API + любому телу, которое может иметь запрос).

Имейте в виду, что вам никогда не придется самостоятельно предоставлять аргументы редюсеру. Это обрабатывается хуком useReducer автоматически: состояние известно, а действие является просто аргументом диспетчера , который передается редюсеру в качестве второго аргумента.

Состояние имеет любой желаемый формат (обычно это объект, но на самом деле это может быть что угодно). Действие также может быть любым, что вы хотите, но есть некоторые очень часто используемые соглашения о том, как его структурировать, и я советую вам следовать этим соглашениям — мы узнаем о них позже. По крайней мере, до тех пор, пока вы не ознакомитесь с ними и не будете уверены, что отказ от них — это действительно то, что вам нужно.

Итак, условно действие — это объект с одним обязательным свойством и одним необязательным свойством:

  • тип — обязательное свойство (аналогично конечной точке API).Он сообщает редьюсеру, какую часть логики он должен использовать для изменения состояния.
  • полезная нагрузка является необязательным свойством (аналогично телу запроса HTTP POST, если таковой имеется). Он предоставляет редюсеру дополнительную информацию о том, как изменить состояние.

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

 
постоянное состояние = {количество: 0};


const action = {тип: 'приращение', полезная нагрузка: 2};
  

Редьюсеры обычно структурированы с оператором switch для действия типа , например:

  const reducer = (state, action) => {
  переключатель (действие.тип) {
    случай 'приращение':
      return { count: state.count + action.payload };
    случай 'уменьшение':
      return { count: state.count - action.payload };
    случай «сброс»:
      вернуть {количество: 0};
  }
};
  

В этом примере редуктор принимает три типа действий: «приращение», «уменьшение» и «сброс».И «приращение», и «уменьшение» требуют полезной нагрузки действия, которая будет определять величину, на которую счетчик увеличивается или уменьшается. Напротив, тип «сброс» не требует никакой полезной нагрузки, поскольку он сбрасывает счетчик обратно на 0.

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

Как работает функция отправки?

Если вы поняли, как работает редюсер, понять функцию диспетчеризации будет довольно просто.

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

Используя наш последний пример редуктора, если бы мы хотели создать кнопку, которая уменьшает счетчик на 2 при нажатии, это выглядело бы так:

  
  

И если мы хотим иметь кнопку, которая сбрасывает счетчик на 0, все еще используя наш последний пример, вы можете опустить полезную нагрузку :

  
  

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

Info

Если вы немного не понимаете последний абзац, я помогу вам в этой статье о массивах зависимостей!

useReducer начальное состояние

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

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

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

В нашем противоположном примере начальное состояние было просто:

 
const InitialState = {количество: 0};

· · ·


const [состояние, отправка] = useReducer (редуктор, initialState);
  

Мы увидим больше подобных примеров в будущем.

useReducer советы и рекомендации

Есть несколько способов улучшить использование useReducer . Некоторые из этих вещей вы действительно должны делать, другие больше зависят от вашего личного вкуса.

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

Редуктор должен выдавать ошибку для неизвестных типов действий

В нашем встречном примере у нас был оператор switch с тремя вариантами: «инкремент», «декремент» и «сброс».Если вы написали это в своем редакторе кода, вы могли заметить, что ESLint злится на вас.

Подсказка

У вас есть ESLint, верно? Если вы этого не сделаете, вы действительно должны настроить его!

ESLint (справедливо) хочет, чтобы операторы switch имели регистр по умолчанию. Итак, каким должен быть редуктор по умолчанию, когда он обрабатывает неизвестный тип действия?

Некоторым нравится просто возвращать состояние:

  const reducer = (state, action) => {
  переключатель (действие.тип) {
    случай 'приращение':
      вернуть {количество: состояние.count + action.payload };
    случай 'уменьшение':
      return { count: state.count - action.payload };
    случай «сброс»:
      вернуть {количество: 0};
 

по умолчанию:

состояние возврата;

} };

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

Вместо этого ваш редуктор по умолчанию должен выдавать ошибку:

  const reducer = (state, action) => {
  переключатель (действие.тип) {
    случай 'приращение':
      return { count: state.count + action.payload };
    случай 'уменьшение':
      return { count: state.count - action.payload };
    случай «сброс»:
      вернуть {количество: 0};
 

по умолчанию:

выдать новую ошибку (`Неизвестный тип действия: ${action.type}`);

} };

Таким образом, вы не пропустите опечатку и не забудете о деле.

Вы должны распространять состояние в каждом действии

До сих пор мы видели только очень простой пример useReducer , в котором состояние является объектом только с одним свойством. Однако обычно вариантов использования useReducer вызывают объекты состояния, по крайней мере, с несколькими свойствами.

Обычно useReducer используется для обработки форм. Вот пример с двумя полями ввода, но вы можете представить то же самое с большим количеством полей.

(Осторожно! В приведенном ниже коде есть ошибка.Сможете заметить?)

  import { useReducer } from 'react';

константное начальное значение = {
  имя пользователя: '',
  электронное письмо: '',
};

const reducer = (состояние, действие) => {
  переключатель (действие.тип) {
    случай 'имя пользователя':
      вернуть {имя пользователя: action.payload};
    case 'электронная почта':
      return {электронная почта: action.payload};
    дефолт:
      throw new Error(`Неизвестный тип действия: ${action.type}`);
  }
};

константная форма = () => {
  const [состояние, отправка] = useReducer (редуктор, начальное значение);
  возвращение (
    <дел>
      <ввод
        тип = "текст"
        значение = {состояние.имя пользователя}
        onChange={(событие) =>
          диспетчеризация ({ тип: 'имя пользователя', полезная нагрузка: event.target.value })
        }
      />
      <ввод
        тип = "электронная почта"
        значение = {state.email}
        onChange={(событие) =>
          рассылка ({ тип: 'электронная почта', полезная нагрузка: event.target.value })
        }
      />
    
); }; экспорт формы по умолчанию;

Ошибка в редьюсере: обновление имени пользователя полностью отменит предыдущее состояние и удалит адрес электронной почты (а обновление адреса электронной почты сделает то же самое с именем пользователя ).

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

  import { useReducer } from 'react';

константное начальное значение = {
  имя пользователя: '',
  электронное письмо: '',
};

const reducer = (состояние, действие) => {
  переключатель (действие.тип) {
    случай 'имя пользователя':
 

return {...состояние, имя пользователя: action.payload};

случай 'электронная почта':

return { ...состояние, электронная почта: действие.полезная нагрузка };

по умолчанию: throw new Error(`Неизвестный тип действия: ${action.type}`); } }; константная форма = () => { const [состояние, отправка] = useReducer (редуктор, начальное значение); возвращение ( <дел> <ввод значение={состояние.имя пользователя} onChange={(событие) => диспетчеризация ({ тип: 'имя пользователя', полезная нагрузка: event.target.value }) } /> <ввод значение = {state.email} onChange={(событие) => диспетчеризация ({ тип: 'электронная почта', полезная нагрузка: событие.целевое значение }) } />
); }; экспорт формы по умолчанию;

Этот пример можно оптимизировать еще больше. Вы могли заметить, что мы немного повторяемся в редюсере: оба случая username и email имеют по сути одинаковую логику. Это не так уж плохо для двух полей, но мы могли бы иметь гораздо больше.

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

  import { useReducer } from 'react';

константное начальное значение = {
  имя пользователя: '',
  электронное письмо: '',
};

const reducer = (состояние, действие) => {
  переключение (действие.тип) {
    случай 'текстовый ввод':
 

возврат {

...состояние,

[action.payload.key]: action.payload.value,

};

по умолчанию: throw new Error(`Неизвестный тип действия: ${action.type}`); } }; константная форма = () => { const [состояние, отправка] = useReducer (редуктор, начальное значение); возвращение ( <дел>

<ввод значение={состояние.имя пользователя} onChange={(событие) => отправлять({ тип: 'текстовый ввод', полезная нагрузка: {ключ: 'имя пользователя', значение: событие.целевое значение }, }) } />

<ввод значение = {state.email} onChange={(событие) => отправлять({ тип: 'текстовый ввод', полезная нагрузка: {ключ: 'электронная почта', значение: event.target.value}, }) } />

); }; экспорт формы по умолчанию;

Как видите, теперь у нас остался только один тип действия: textInput . Полезная нагрузка действия также изменилась — она стала объектом с ключом (свойство для обновления) и значением (значение для обновления ключа ).

Довольно аккуратно, если вы спросите меня!

Совет

Вы могли заметить, что в этом коде есть еще одно место, где мы повторяемся: обработчик события onChange . Единственное, что изменилось, это payload.key .

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

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

Это очень распространенный шаблон, и мы покажем его пример позже в этой статье.

Придерживайтесь стандартной структуры действия

Под «обычной структурой действия» я подразумеваю структуру, которую мы использовали до сих пор в этой статье: действие должно быть литералом объекта с обязательным типом и полезная нагрузка .

Это способ структурирования действий в Redux, а также наиболее часто используемый. Это опробовано и протестировано, и это очень хорошее значение по умолчанию для всех ваших useReducer s.

Основной недостаток этой структуры в том, что иногда она может быть немного многословной. Но если вам не очень удобно использовать useReducer , я бы посоветовал вам придерживаться пути Redux.

Синтаксис Sugar: деконструировать
ввести и полезную нагрузку из действия

Это качество жизни. Вместо того, чтобы повторять action.payload (и, возможно, action.type ) везде в редьюсере, вы можете напрямую деконструировать второй аргумент редюсера, например:

  

const reducer = (state, { type, payload }) => {

переключатель (тип) {

случай «приращение»:

возврат {количество: состояние.количество + полезная нагрузка };

случай «уменьшение»:

возврат {количество: state.count - полезная нагрузка};

случай «сброс»:

возврат {количество: 0};

по умолчанию:

выдать новую ошибку (`Неизвестный тип действия: ${тип}`);

} };

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

  

const reducer = ({количество}, {тип, полезная нагрузка}) => {

переключатель (тип) { случай 'приращение':

возврат {количество: количество + полезная нагрузка};

случай «уменьшение»:

возврат {количество: количество - полезная нагрузка};

случай «сброс»:

возврат {количество: 0};

по умолчанию: throw new Error(`Неизвестный тип действия: ${type}`); } };

Вот и все советы и рекомендации!

useReducer третий параметр: ленивая инициализация

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

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

useState vs useReducer : когда и что использовать

Теперь, когда вы знаете, как работает useReducer и как использовать его в своих компонентах, нам нужно решить важный вопрос. Поскольку useState и useReducer — это два способа управления состоянием, какой из них следует выбрать, когда?

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

Во-первых, знайте, что useState должен оставаться вашим выбором по умолчанию для управления состоянием React. Переключитесь на useReducer только в том случае, если у вас возникнут проблемы с useState (и если эту проблему можно решить, переключившись на useReducer ). По крайней мере, до тех пор, пока у вас не будет достаточно опыта работы с useReducer , чтобы заранее знать, какой из них использовать.

Я проиллюстрирую, когда использовать useReducer вместо useState на нескольких примерах.

Несколько частей состояния, которые зависят друг от друга

Один из хороших вариантов использования useReducer — это когда у вас есть несколько частей состояния, которые зависят друг от друга.

Это довольно распространено при создании форм. Допустим, у вас есть ввод текста, и вы хотите отслеживать три вещи:

  1. Значение ввода.
  2. Был ли вход уже "тронут" пользователем. Это полезно, чтобы знать, отображать ли ошибку. Например, если поле является обязательным, вы хотите отображать ошибку, когда оно пустое.Однако вы не хотите отображать ошибку при первом рендеринге, если пользователь никогда раньше не посещал ввод.
  3. Есть ли ошибка.

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

С useReducer логика довольно проста:

  import { useReducer } from 'react';

константное начальное значение = {
  ценность: '',
  коснулся: ложь,
  ошибка: ноль,
};

const reducer = (состояние, { тип, полезная нагрузка }) => {
  переключатель (тип) {
    дело «обновление»:
      возвращение {
        значение: полезная нагрузка.ценность,
        коснулся: правда,
        ошибка: полезная нагрузка.ошибка,
      };
    случай «сброс»:
      вернуть начальное значение;
    дефолт:
      throw new Error(`Неизвестный тип действия: ${type}`);
  }
};

константная форма = () => {
  const [состояние, отправка] = useReducer (редуктор, начальное значение);
  console.log(состояние);
  возвращение (
    <дел>
      <ввод
        className={состояние.ошибка? 'ошибка' : ''}
        значение = {состояние. значение}
        onChange={(событие) =>
          отправлять({
            тип: 'обновление',
            полезная нагрузка: {
              значение: событие.целевое значение,
              ошибка: state.touched? event.target.value.length === 0 : ноль,
            },
          })
        }
      />
      
    
); }; экспорт формы по умолчанию;

Добавьте немного элементарного CSS для стилизации класса error , и вы получите начало ввода с хорошим UX и простой логикой благодаря useReducer :

  .error {
  цвет границы: красный;
}

.ошибка: фокус {
  цвет контура: красный;
}
  

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

Еще один хороший вариант использования useReducer — это когда у вас МНОГО различных частей состояния, и помещение их всех в useState действительно выйдет из-под контроля.

Ранее мы видели пример одного редюсера, управляющего двумя входами одним и тем же действием. Мы можем легко масштабировать этот пример до 4 входов.

Пока мы это делаем, мы могли бы также реорганизовать действие каждого отдельного input :

  import { useReducer } from 'react';

константное начальное значение = {
  имя: '',
  фамилия: '',
  имя пользователя: '',
  электронное письмо: '',
};

const reducer = (состояние, действие) => {
  переключение (действие.тип) {
    дело «обновление»:
      возвращение {
        ...государство,
        [action.payload.key]: action.payload.value,
      };
    дефолт:
      throw new Error(`Неизвестный тип действия: ${action.type}`);
  }
};

константная форма = () => {
  const [состояние, отправка] = useReducer (редуктор, начальное значение);

  const inputAction = (событие) => {
    отправлять({
      тип: 'обновление',
      полезная нагрузка: {ключ: event.target.name, значение: event.target.value},
    });
  };

  возвращение (
    <дел>
      <ввод
        значение = {состояние.имя}
        тип = "текст"
        имя = «имя»
        onChange={входное действие}
      />
      <ввод
        значение = {state.lastName}
        тип = "текст"
        имя = "фамилия"
        onChange={входное действие}
      />
      <ввод
        значение={состояние.имя пользователя}
        тип = "текст"
        onChange={входное действие}
        имя = "имя пользователя"
      />
      <ввод
        значение = {state.email}
        тип = "электронная почта"
        имя = "электронная почта"
        onChange={входное действие}
      />
    
); }; экспорт формы по умолчанию;

Серьезно, насколько чист и ясен этот код? Представьте, что вы делаете это с 4 useState вместо этого! Хорошо, не будет таким плохим, как , но его можно масштабировать до нужного количества входных данных, не добавляя ничего, кроме самих входных данных.

И вы также можете легко развить это. Например, мы можем захотеть добавить свойства touched и error последнего раздела к каждому из четырех входов в этом разделе.

На самом деле, я советую вам попробовать это самостоятельно, это хорошее упражнение для закрепления ваших знаний!

Как насчет того, чтобы сделать это, но вместо этого использовать

useState ?

Один из способов избавиться от дюжины операторов useState — просто поместить все ваше состояние в один объект, хранящийся в одном useState , а затем обновить его.

Это решение работает, и иногда это хороший способ. Но вы часто обнаружите, что повторно реализуете useReducer более неуклюжим способом. Можно сразу использовать редуктор.

useReducer с TypeScript

Хорошо, теперь вы должны освоить useReducer . Если вы являетесь пользователем TypeScript, вам, вероятно, интересно, как правильно сделать так, чтобы они хорошо играли.

К счастью, это довольно просто. Вот оно:

  import {useReducer, ChangeEvent} из 'реакции';

 

тип Состояние = {

firstName: строка;

фамилия: строка;

имя пользователя: строка;

электронная почта: строка;

};

тип Действие =

| {

тип: «обновление»;

полезная нагрузка: {

ключ: строка;

значение: строка;

};

}

| {тип: 'сбросить'};

константное начальное значение = { имя: '', фамилия: '', имя пользователя: '', электронное письмо: '', };

const reducer = (состояние: состояние, действие: действие) => {

переключатель (действие.тип) { дело «обновление»: return { ...state, [action.payload.key]: action.payload.value }; случай «сброс»: вернуть начальное значение; дефолт: throw new Error(`Неизвестный тип действия: ${action.type}`); } }; константная форма = () => { const [состояние, отправка] = useReducer (редуктор, начальное значение); const inputAction = (событие: ChangeEvent) => { отправлять({ тип: 'обновление', полезная нагрузка: {ключ: event.target.name, значение: event.target.value}, }); }; возвращение ( <дел> <ввод значение = {состояние.имя} тип = "текст" имя = «имя» onChange={входное действие} /> <ввод значение = {state.lastName} тип = "текст" имя = "фамилия" onChange={входное действие} /> <ввод значение={состояние.имя пользователя} тип = "текст" onChange={входное действие} имя = "имя пользователя" /> <ввод значение = {state.email} тип = "электронная почта" имя = "электронная почта" onChange={входное действие} /> ); }; экспорт формы по умолчанию;

Если вы не знакомы с синтаксисом типа Действие , это размеченное объединение.

Redux: мощный

useReducer

Мы заканчиваем наше руководство по useReducer (уф, оно оказалось намного длиннее, чем я ожидал!). Есть еще одна важная вещь, которую следует упомянуть: Redux.

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

На самом деле, вы можете думать о Redux как о большом, глобальном, управляемом и оптимизированном useReducer для всего вашего приложения. Это действительно все, что есть.

У вас есть «хранилище», которое является вашим состоянием, и вы определяете «действия», которые сообщают «редуктору», как изменить это хранилище. Звучит знакомо!

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

Подведение итогов

Вот и конец статьи! Я надеюсь, что это помогло вам узнать все, что вы хотели о useReducer .

Как вы видели, это может быть очень мощный инструмент в вашем наборе инструментов React.

Удачи!

Переходники · ReduxKotlin

Редьюсеры определяют, как изменяется состояние приложения в ответ на отправленные действия в магазин. Помните, что действия описывают только то, что произошло , но не описывают, как меняется состояние приложения.

Проектирование формы состояния

В Redux все состояние приложения хранится как один объект.Это хорошая идея, чтобы подумать о его форма перед написанием любого кода. Каково минимальное представление состояния вашего приложения в виде объекта?

Для нашего приложения todo мы хотим хранить две разные вещи:

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

  класс данных AppState(
    val visibilityFilter: VisibilityFilters = VisibilityFilters.ПОКАЗАТЬ ВСЕ,
    val todos: List = listOf(
        Todo(text = "Рассмотрите возможность использования Redux",
            завершено = верно),
        Todo(text = "Сохранить все состояния в одном дереве",
            завершено = неверно)
    )
)
  
Примечание о взаимоотношениях

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

Действия по обработке

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

  typealias Reducer = (state: State, action: Any) -> State
  

Существует как минимум 2 способа определения редукторов:

  1. функции
  забавный редуктор (состояние: AppState, действие: Any): AppState {
   
   вернуть новое состояние приложения
}
  
  1. функциональные объекты - TODO правильно сформулирован?
  редуктор val: Reducer = {состояние, действие ->
    
    новое состояние приложения
}
  

Это называется редуктор, потому что это тип функции, которую вы бы передали Массив.уменьшить(операция: (соотв: S, T) -> S) . Очень важно, чтобы редуктор оставался чистым. Вещи, которые вы не должны никогда делать внутри редуктора:

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

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

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

Примечание об отличиях от JS Redux

JS Redux позволяет инициализировать хранилище без предварительно загруженного состояния, опуская preloadedState параметр из функции createStore . ReduxKotlin требует, чтобы предварительно загруженное состояние было передано в создать хранилище . Это позволяет нам использовать ненулевой тип для State.

  val store = createThreadSafeStore (редуктор, INITIAL_STATE)
  

Теперь давайте обработаем SET_VISIBILITY_FILTER .Все, что нужно сделать, это изменить visibilityFilter на государство. Легко:

 
fun todosReducer (состояние: AppState, действие: Any) =
    когда (действие) {
        это SetVisibilityFilter -> state.copy(visibilityFilter = action.visibilityFilter)
        иначе -> состояние
    }
  

Обратите внимание:

  1. Мы не изменяем состояние . Мы создаем копию с помощью функции copy для классов данных. Новый объекты состояния также могут быть созданы с помощью конструктора класса State, но копия обычно это удобный способ изменить только часть состояния.

  2. Мы возвращаем предыдущее состояние в случае else . Важно вернуть предыдущее состояние для любого неизвестного действия.

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

Дополнительные действия

Нам нужно обработать еще два действия! Как и в случае с SetVisibilityFilter , мы расширим наш редуктор на ручку AddTodo .

  fun todosReducer (состояние: AppState, действие: Any) =
    когда (действие) {
        это SetVisibilityFilter -> state.copy(visibilityFilter = action.visibilityFilter)
        это AddTodo -> state.copy(todos = state.todos.plus(
                        Сделать(
                            текст = действие.текст,
                            завершено = ложь
                            )
                        ))
        иначе -> состояние
    }
  

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

Наконец, реализация обработчика ToggleTodo не должна стать полной неожиданностью:

  — это ToggleTodo -> state.mapIndexed { index, todo ->
            если (индекс == действие.индекс) {
                todo.copy(completed = !todo.completed)
            } еще {
                сделать
            }
        }
  
Примечание по именованию редуктора

В JS Redux полезно называть функции редуктора тем же именем, что и поле состояния, которое они будут использовать. handle из-за функций JS, которые позволяют функции combReducers назначать редюсеры полям.Например:

  const todoApp = combReducers({
  фильтр видимости,
  дела
})
  

Это не функция ReduxKotlin из-за ее статического типа. Рекомендуется использовать более описательное имя, например TodoReducer , чтобы четко идентифицировать функцию.

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

Переходники для расщепления

Вот наш код. Это довольно многословно:

  fun todosReducer (состояние: AppState, действие: Any) =
    когда (действие) {
        это SetVisibilityFilter -> state.copy(visibilityFilter = action.visibilityFilter)
        это AddTodo -> state.copy(todos = state.todos.plus(
                        Сделать(
                            текст = действие.текст,
                            завершено = ложь
                            )
                        ))
        это ToggleTodo -> state.copy(
                todos = state.todos.mapIndexed { todo, index ->
                    если (индекс == действие.индекс) {
                        todo.copy(completed = !todo.completed)
                    } еще {
                        сделать
                    }
                }
            )
        иначе -> состояние
    }
  

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

Есть ли способ облегчить понимание? Кажется, что todos и visibilityFilter являются обновляется полностью самостоятельно. Иногда поля состояния зависят друг от друга и т.д. требуется рассмотрение, но в нашем случае мы можем легко разделить обновление todos на отдельный функция:

  fun todosReducer (состояние: List, действие: Any) =
    когда (действие) {
        это AddTodo -> state.plus(
                        Сделать(
                            текст = действие.текст,
                            завершено = ложь
                            )
                        )
        это ToggleTodo -> state.mapIndexed { todo, index ->
                    если (индекс == действие.индекс) {
                        todo.copy(completed = !todo.completed)
                    } еще {
                        сделать
                    }
                }
        иначе -> состояние
    }
  

Обратите внимание, что todosReducer также принимает состояние , но состояние является списком! Теперь todoReducer дает todos просто часть состояния для управления, а todosReducer знает, как обновить только эту часть. Это называется композицией редуктора и является фундаментальным шаблоном создания приложений Redux.

  забавная видимостьFilterReducer (состояние: VisibilityFilter, действие: любое): VisibilityFilter =
    когда (действие) {
        это SetVisibilityFilter -> action.visibilityFilter
        иначе -> состояние
    }
  

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

  fun todosReducer (состояние: List, действие: Any): List =
    когда (действие) {
        это AddTodo -> state.plus(
                        Сделать(
                            текст = действие.текст,
                            завершено = ложь
                            )
                        )
        это ToggleTodo -> state.mapIndexed { todo, index ->
                    если (индекс == действие.индекс) {
                        todo.copy(completed = !todo.завершенный)
                    } еще {
                        сделать
                    }
                }
        иначе -> состояние
    }

забавная видимостьFilterReducer (состояние: VisibilityFilter, действие: любое): VisibilityFilter =
    когда (действие) {
        это SetVisibilityFilter -> action.visibilityFilter
        иначе -> состояние
    }
    
весело rootReducer (состояние: состояние приложения, действие: любое) = состояние приложения (
    todos = todosReducer (состояние.todos, действие),
    видимостьФильтр = видимостьФильтрРедусер(состояние.видимостьФильтр, действие)
)
  

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

Это уже хорошо выглядит! Когда приложение больше, мы можем разделить редьюсеры на отдельные файлы. и сохранить их полностью независимыми и управлять различными областями данных.

Примечание по комбинированию шаблона редуктора

Соединение редукторов вручную приводит к некоторому шаблону.Одна альтернатива Паттер для ReduxKotlin использует интерфейс Reducible. Как редукторы разделены, зависит от вас и вашей команды, и рекомендуется соблюдать последовательность в том, как это обрабатывается на протяжении всего проекта. В JS Redux CombineReducers имеет большое значение для облегчения этого стандартного шаблона, однако со статическим набрал Kotlin эту функцию не так-то просто реализовать. Есть возможность использования созданный код и аннотации, чтобы помочь в этой области.

ReduxKotlin имеет функцию CombineReducers , однако она объединяет редьюсеры только одного и того же государственный тип.Он довольно ограничен по сравнению с JS combReducers .

Следующие шаги

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

Управление сложностью в Redux: редукторы высшего порядка и асинхронное состояние

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

Примечание: Пост будет не о саги или эпики – которые связаны с управлением асинхронное состояние, но и вообще с управлением эффектами — но подробнее о полезном Паттерны Redux, рассказанные на примере асинхронного управления состоянием.

Итак, мы хотим создать приложение?

Хорошая идея! В любом случае, что еще мы собираемся делать с этими странными грудами кремния и пластика.И мы построим его с помощью Redux, вдохновленного Flux? Очень элегантный и ремонтопригодный! По крайней мере, мы на это надеемся. Все примеры в сети определенно выглядят именно так. Тем не менее, когда мы начинаем строить, мы быстро сталкиваемся с проблемами, которые, кажется, выходят за рамки этих простых примеров, и мы начинаем чувствовать себя все более и более неуверенными в том, как лучше всего двигаться вперед. Что еще хуже, посреди всего этого мы обнаруживаем, что наше приложение на самом деле становится все более в элегантным и и удобным в сопровождении. Кажется, что даже простые изменения требуют множества действий, редюсеров, селекторов и компонентов.В чем дело? Была ли эта штука с Redux всеобщей шумихой и шумихой?

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

Как мы вообще сюда попали

Ты не помнишь? Что ж, мы начали с простой и благородной цели: создать приложение, которое поможет людям инвентаризировать продукты на их кухнях. Все началось с создателя действия:

  const addFoodItem = item => ({ type: "ADD_FOOD_ITEM", payload: item })  

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

  const reducer = (состояние, { тип, полезная нагрузка }) => {
  переключатель (тип) {
    чехол "ADD_FOOD_ITEM":
      возвращение {
        продукты питания: [...state.foodItems, полезная нагрузка],
      }
    дефолт:
      возвращаемое состояние
  }
}  

Что, в свою очередь, было обработано (мы используем React):

  const App = ({ foodItems, addFoodItem }) => (
  <дел>
    <ул>
      {foodItems.map(({название, количество}) => (
        
  • {имя}, количество: {количество}
  • ))}

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

    (и на самом деле мы не будем затрагивать тему обработки форм в этом посте — это еще один распространенный источник сложности в приложениях Redux — кроме как указывать и хрюкать в общем направлении Redux Form)

    OK и так где мы конкретно?

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

      const FOOD_ITEMS_FETCH_PENDING = "FOOD_ITEMS_FETCH_PENDING"
    const FOOD_ITEMS_FETCH_COMPLETE = "FOOD_ITEMS_FETCH_COMPLETE"
    константа FOOD_ITEMS_FETCH_ERROR = "FOOD_ITEMS_FETCH_ERROR"
    константа FOOD_ITEMS_ADD_PENDING = "FOOD_ITEMS_ADD_PENDING"
    const FOOD_ITEMS_ADD_COMPLETE = "FOOD_ITEMS_ADD_COMPLETE"
    константа FOOD_ITEMS_ADD_ERROR = "FOOD_ITEMS_ADD_ERROR"
    
    
    
    const addFoodItem = элемент => отправка => {
      отправка ({ тип: FOOD_ITEMS_ADD_PENDING })
    
      едаСервис
        .добавитьFoodItem(предмет)
        .then (полезная нагрузка => отправка ({ тип: FOOD_ITEMS_ADD_COMPLETE, полезная нагрузка }))
        .catch (полезная нагрузка => отправка ({ тип: FOOD_ITEMS_ADD_ERROR, полезная нагрузка }))
    }
    
    константа fetchFoodItems = элемент => отправка => {
      отправка ({ тип: FOOD_ITEMS_FETCH_PENDING })
    
      едаСервис
        .fetchFoodItems(предмет)
        .then (полезная нагрузка => отправка ({ тип: FOOD_ITEMS_FETCH_COMPLETE, полезная нагрузка }))
        .catch (полезная нагрузка => отправка ({ тип: FOOD_ITEMS_FETCH_ERROR, полезная нагрузка }))
    }
    
    
    
    const reducer = (состояние, { тип, полезная нагрузка }) => {
      переключатель (тип) {
        дело FOOD_ITEMS_FETCH_PENDING:
          возвращение {
            ...государство,
            foodItemsLoaded: ложь,
          }
        дело FOOD_ITEMS_FETCH_ERROR:
          возвращение {
            ...государство,
            foodItemsLoaded: ложь,
            foodItemsError: полезная нагрузка,
          }
        случай FOOD_ITEMS_FETCH_COMPLETE:
          возвращение {
            ...государство,
            foodItemsLoaded: правда,
            foodItems: полезная нагрузка,
          }
        случай FOOD_ITEMS_ADD_PENDING:
          возвращение {
            ...государство,
            foodItemsДобавление: правда,
          }
        дело FOOD_ITEMS_ADD_ERROR:
          возвращение {
            ...государство,
            foodItemsДобавление: false,
          }
        случай FOOD_ITEMS_ADD_COMPLETE:
          возвращение {
            ...государство,
            foodItemsДобавление: false,
            foodItems: [...state.foodItems, полезная нагрузка],
          }
        дефолт:
          возвращаемое состояние
      }
    }
    
    
    
    const App = ({ foodItems, foodItemsLoaded, foodItemsAdding, addFoodItem }) =>
      едаПредметыЗагружено ? (
        <дел>
          <ул>
            {foodItems.map(({название, количество}) => (
              
  • {имя}, количество: {количество}
  • ))} <кнопка onClick={() => addFoodItem({ имя: "🍋", количество: 50 })} disabled={foodItemsAdding} > Добавить продукт питания {foodItemsAdding ? <Спиннер/> : ""} ) : ( <Спиннер/> )

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

    Как нам выбраться отсюда

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

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

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

    Что нам нужно сделать (и этот прием можно найти в предыдущей ссылке на документ, а также здесь и здесь) — это написать создатели и редьюсеры более высокого порядка, т.е. создатели действий и редьюсеры .

    Создание многоразового генератора действий

    Как мы создаем действия? Что ж, с функциями, которые производят действия (a.к.а. создатели акции). Итак, как мы можем создать генераторы действий? Ну, с функциями, которые производят функции, которые производят действия! (Или которые производят преобразователи в нашем случае)

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

     
    
    
    const asyncActionCreator = (asyncTypes, createThunk) => (...args) => {
      const thunk = createThunk(...args)
    
      обратная отправка => {
        диспетчеризация ({тип: asyncTypes.pending})
    
        
        
        
        
        обратная отправка (thunk)
          .тогда (полезная нагрузка => ({
            тип: asyncTypes.complete,
            полезная нагрузка,
          }))
          .поймать (ошибка => ({
            тип: asyncTypes.error,
            ошибка: правда,
            полезная нагрузка: ошибка,
          }))
      }
    }  

    Теперь всякий раз, когда нам нужен создатель действия для наших асинхронных действий, мы просто делаем следующее:

      const fetchFoodItems = createActionCreator(
      {
        в ожидании: FOOD_ITEMS_FETCH_PENDING,
        завершено: FOOD_ITEMS_FETCH_COMPLETE,
        ошибка: FOOD_ITEMS_FETCH_ERROR,
      },
      () => () => едаСервис.fetchFoodItems()
    )
    
    константа addFoodItem = createActionCreator(
      {
        в ожидании: FOOD_ITEMS_ADD_PENDING,
        завершено: FOOD_ITEMS_ADD_COMPLETE,
        ошибка: FOOD_ITEMS_ADD_ERROR,
      },
      
      
      товар => отправка =>
        foodService.addFoodItem(item).then(result => {
          диспетчеризация (recordAnalytics ({ событие: "OMG_SOMEONES_USING_THE_APP" }))
          вернуть результат
        })
    )  

    Легко!

    Примечание. У нас также есть другие варианты. Вместо создания нескольких действий типы, мы могли бы прикрепить дополнительную информацию к действиям одного тип. action.name , например, может использоваться редуктором поверх FOOD_ITEMS_FETCH различать асинхронное состояние. Кроме того, мы могли бы использовать промежуточное ПО на месте действия высшего порядка создатель (прокрутите до определения callAPIMiddleware ), чтобы организовать асинхронное состояние. депеши. Все возможные варианты!

    Еще одно примечание: этот конкретный шаблон предполагает соответствие 1 к 1 между Вызовы и ресурсы API. Если вы используете normalizr это не обязательно быть дело.Одно (потенциально наивное) решение может состоять в том, чтобы написать action создатели, которые просто отправляют больше действий: вместо 3 для одного ресурс, 3 для каждого ресурса в запросе. В случае Falcor или GraphQL, вероятно, было бы проще всего отслеживать состояние загрузки представления, а не ресурса состояние загрузки.

    Создание редуктора многократного использования

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

     
    
    
    const loadStateReducer = (asyncStates) => {
      return (состояние = {загрузка: ложь, загрузка: ложь, данные: ноль}, действие) {
        const asyncState = asyncStates[action.тип]
    
        переключатель (асинхронное состояние) {
          дело «в ожидании»:
            возвращение {
              загрузка: правда,
              загружено: ложь,
              данные: состояние.данные
            }
          случай «ошибка»:
            возвращение {
              загрузка: ложная,
              загружено: ложь,
              ошибка: action.payload,
              данные: состояние.данные
            }
          дело «завершено»:
            возвращение {
              загрузка: ложная,
              загружено: правда,
              данные: action.payload
            }
          дефолт:
            возвращаемое состояние
        }
      }
    }  

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

      const { combReducers } = require("redux")
    
    константа foodItemsLoadState = loadStateReducer({
      FOOD_ITEMS_FETCH_PENDING: "в ожидании",
      FOOD_ITEMS_FETCH_ERROR: "ошибка",
      FOOD_ITEMS_FETCH_COMPLETE: "завершено",
    })
    
    const foodItemAddState = loadStateReducer({
      FOOD_ITEMS_ADD_PENDING: "в ожидании",
      FOOD_ITEMS_ADD_ERROR: "ошибка",
      FOOD_ITEMS_ADD_COMPLETE: "завершено",
    })
    
    const foodItemAdd = (состояние, { тип, полезная нагрузка }) => {
      переключатель (тип) {
        случай FOOD_ITEMS_ADD_COMPLETE:
          возвращение { ...состояние, данные: [...состояние.данные, полезная нагрузка] }
        дефолт:
          возвращаемое состояние
      }
    }
    
    const foodItems = (состояние, действие) =>
      foodItemAdd (foodItemsLoadState (состояние, действие), действие)
    
    константный редуктор = combReducers({
      продукты питания,
      едаItemAddState,
    })  

    Таким образом, мы внезапно достигли чего-то даже более фундаментального, чем повторное использование кода: мы создали абстракцию данных! Теперь у нас есть тип де-факто, назовем его «асинхронные данные», который, как мы знаем, может находиться в одном из 4 состояний: неинициировано, в ожидании, с ошибкой и завершено.Мы можем написать полностью универсальные функции для обработки этих данных, мы можем повторно использовать эти функции во всем нашем приложении, мы можем отделить эту логику от нашей предметной/бизнес-логики, и, что очень важно, мы можем согласиться с хорошим афоризмом из классного специалист по информатике:

    Лучше иметь 100 функций, работающих с одной структурой данных, чем 10 функций с 10 структурами данных.

    В целом все хорошо. Мы идем домой и спим спокойно.

    И для подозрительных: эти редукторы могут пахнуть несовершенством! Зачем например, хранит ли редуктор foodItemAddState полезную нагрузку данных , когда все мы хотим от него это состояние загрузки? И почему foodItemAdd нужно знать о нашей асинхронной форме данных, когда все, о чем она должна заботиться, это приложение данные? Истинный! Однако важно то, что мы перешли от меньшего к более компонуемый шаблон редуктора.Если позже мы решим, что это была плохая идея вложите полезные данные в их состояния загрузки и хотите дать им свои собственные срезы состояния, нам будет легче это сделать. (Мы могли бы также, образом, измените loadStateReducer , чтобы принять другой редуктор в качестве параметра и использовать что для уменьшения всего данных полезной нагрузки)

    Пожинаем плоды

    Проснувшись от нашего мирного сна, мы чувствуем себя свежими и бодрыми! Программное обеспечение съест мир! И мы реализуем все функции! Мы видим версию 3.0 нашего приложения четко перед нашим внутренним взором: оно делает практически все. (И вскоре он может производить достаточно тепла, чтобы готовить еду)

    Подготовка к величию

    Состояния загрузки нашего нового приложения, конечно, будут более сложными, чем раньше. Например, что мы будем делать, когда у нас есть представления, зависящие от нескольких асинхронных источников данных? (В настоящее время каждому компоненту списка необходимо отслеживать состояние загрузки только одного из них). А как насчет компонентов, которые зависят от агрегирования или сопоставления этих асинхронных данных?

    Что касается первой проблемы (то есть проблемы с ожиданием рендеринга до тех пор, пока не будет загружено X зависимостей), мы думаем о том, чтобы позволить компонентам обрабатывать совокупное состояние загрузки.Они получат несколько параметров состояния загрузки и выполнят некоторую логическую логику, чтобы определить общее состояние загрузки. Однако это кажется неправильным. В общем случае наши компоненты просто хотят знать, могут ли они отображать или им нужно показывать счетчик.

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

    Написание селекторов для асинхронных данных

    Вместо этого мы понимаем, что состояние загрузки набора ресурсов можно вычислить по состояниям загрузки каждого из них. Другими словами: это производное состояние. И мы знаем, как работать с производным состоянием в Redux: с так называемыми селекторами.

    Начнем с создания селекторов для каждого из наших асинхронных ресурсов:

      const getAsyncFoodItems = state => state.foodItems
    const getAsyncCookware = состояние => состояние.cookware
    
    const getAsyncFunFactsById = state => state.funFactsById  

    Примечание. Посмотрите видео Дэна Абрамова о совместном размещении селекторов с редукторы полезные советы о том, как это настроить

    Теперь мы хотели бы иметь возможность составлять эти селекторы и обрабатывать результат как один «срез» асинхронных данных.Мы хотели бы написать что-то вроде этого:

      const getFoodItemListViewState = createAsyncSelector({
      едаItems: getAsyncFoodItems,
      funFactsById: getAsyncFunFactsById,
    })  

    Или что-то вроде этого:

      const getAsyncFoodItemsWithFacts = createAsyncSelector(
      {
        едаItems: getAsyncFoodItems,
        FunFactsById: получитьFunFactsById
      },
      ({ foodItems, funFactsById }) => (
        объединитьFunFactsIntoFoodItems(foodItems, funFactsById)
      ))
    )  

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

      const FoodList = ({loaded, data}) => {
      если (! загружено) {
        вернуть <Спиннер />
      }
    
      const { foodItemsWithFacts } = данные;
    
      возвращение (
        
      { foodItemsWithFacts.map(({название, факт количества}) => (
    • {имя} количество: {количество

      {факт

    • )) }
    ) }

    Вот одна из возможных реализаций:

    (в примере используется повторный выбор)

      const { createSelector, createStructuredSelector } = require("reselect")
    константная идентичность = х => х
    
    константа createAsyncSelector = (
      асинхронныеселекторы = {},
      синхронизирующиеселекторы = {},
      вычислить = личность
    ) => {
      
      
      
      
      если (аргументы.длина === 2 && typeof syncSelectors === "функция") {
        вычисление = синхронизацияселекторов
        синхронизирующиеселекторы = {}
      }
    
      
      const getAsync = createStructuredSelector(asyncSelectors)
      const getSync = createStructuredSelector(syncSelectors)
      
      const asyncKeys = Object.keys(asyncSelectors)
    
      вернуть createSelector(getAsync, getSync, (asyncData, syncData) => {
        
        
        
        
    
        
        
        если (
          asyncKeys.некоторые(
            ключ =>
              !asyncData[ключ].загрузка &&
              !asyncData[ключ].загружен &&
              !asyncData[ключ].ошибка
          )
        ) {
          вернуть {загрузка: ложь, загружено: ложь, данные: ноль}
        }
    
        
        если (asyncKeys.some(key => asyncData[key].loading)) {
          вернуть {загрузка: истина, загружено: ложь, данные: ноль}
        }
    
        
        если (asyncKeys.some(key => asyncData[key].error)) {
          возвращение {
            загрузка: ложная,
            загружено: ложь,
            ошибка: правда,
          }
        }
    
        
        возвращение {
          загрузка: ложная,
          загружено: правда,
          данные: вычислить({
            
            
            ...синхронизация данных,
            
            
            
            
            ... asyncKeys.reduce((акк, ключ) => {
              акк[ключ] = asyncData[ключ].данные
              возврат согласно
            }),
          }),
        }
      })
    }  

    Мощная штука! Мы решили как проблему представления совокупного состояния загрузки, так и проблему получения состояния из асинхронных данных, и мы сделали это, объединив две проблемы в одну. Эффективный!

    Примечание: в нашем описании не хватает одной важной детали. реализация createAsyncSelector : не сохраняет старые данные полезная нагрузка так же, как и наш редьюсер, что может произойти, например, когда переход из состояния LOADED в LOADING.Но как нам получить старую полезную нагрузку данных если мы внутри чистой функции? Это можно реализовать, создав пользовательский селектор создатель который обертывает значение по умолчанию запоминать функция, так что вы можете сохранить ссылку на запомненный результат и использовать его в вашей функции вывода данных. Фу!

    Заключение

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

    И теперь наше наследие будет продолжаться вечно:

    Что такое класс Hadoop Reducer в MapReduce?

    До сих пор мы подробно рассмотрели введение Hadoop и Hadoop HDFS . В этом руководстве мы предоставим вам подробное описание Hadoop Reducer.

    Здесь мы обсудим, что такое Reducer в MapReduce, как работает Reducer в Hadoop MapReduce, различные этапы работы Hadoop Reducer, как мы можем изменить количество Reducer в Hadoop MapReduce.

    Что такое Hadoop Reducer?

    Reducer в Hadoop MapReduce сокращает набор промежуточных значений, которые имеют общий ключ, до меньшего набора значений.

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

    Между ключами и редукторами при выполнении задания MapReduce выполняется сопоставление «один к одному».Они работают параллельно, поскольку не зависят друг от друга. Пользователь определяет количество редукторов в MapReduce.

    Этапы редюсера Hadoop

    Ниже перечислены три этапа редюсера:

    1. Этап перемешивания

    Это этап, на котором отсортированные выходные данные преобразователя являются входными данными для редуктора. Фреймворк с помощью HTTP извлекает соответствующий раздел вывода всех преобразователей на этом этапе. Фаза сортировки

    2. Фаза сортировки

    Это фаза, на которой входные данные от разных преобразователей снова сортируются на основе похожих ключи в разных картографах.

    Перемешивание и сортировка выполняются одновременно.

    3. Этап сокращения

    Этот этап выполняется после перемешивания и сортировки. Задача Reduce объединяет пары ключ-значение. С помощью свойства OutputCollector.collect() выходные данные задачи сокращения записываются в файловую систему. Выход редуктора не сортируется.

    Количество редукторов в Hadoop MapReduce

    Пользователь задает количество редукторов с помощью свойства Job.setNumreduceTasks(int) .Таким образом нужное количество редукторов по формуле:

    0.95 или 1.75 умножить на (<кол-во узлов> * <количество максимального контейнера на узел>)

    Итак, при 0.95 все редукторы сразу запускаются. Затем начните передачу выходных данных карты, когда карты будут готовы.

    Быстрее узел заканчивает первый раунд редукторов с 1,75. Затем он запускает вторую волну редьюсера, которая гораздо лучше справляется с балансировкой нагрузки.

    С увеличением количества редукторов:

    Заключение

    Следовательно, Reducer принимает выходные данные картографов в качестве входных данных. Затем обработайте пары ключ-значение и создайте выходные данные. Выход редуктора является окончательным выходом. Если вам нравится этот блог или у вас есть какие-либо вопросы, связанные с Hadoop Reducer, поделитесь с нами, оставив комментарий.

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *