Обновления базовых состояний в реакции представляют собой ветерок с использованием SetState
, но обновление глубоко вложенных ценностей в вашем штате может немного сложно. В этом посте я покажу вам, как вы можете использовать линзы в Рамда Чтобы обрабатывать обновления глубокого состояния чистыми и функциональными способами.
Давайте начнем с простого счетчика.
import React from 'react'; import { render } from 'react-dom'; class App extends React.Component { constructor(props) { super(props) this.state = { count: 0 } this.increase = this.increase.bind(this) this.decrease = this.decrease.bind(this) } increase() { this.setState((state) => ({count: state.count + 1})) } decrease() { this.setState((state) => ({count: state.count - 1})) } render() { return () } } render({this.state.count}, document.getElementById('root'));
Здесь мы используем функцию как аргумент для SetState
и просто увеличивать или уменьшение количества на основе прохожденного в государственной стоимости. Это хорошо для простого свойства, сидящего на верхнем уровне штата дерево, но давайте обновим форму нашего государственного объекта и переместите это Считать
немного глубже в государство.
this.state = { a: { name: 'pointless structure', b : { stuff: 'things', count: 0 } } }
Это новое Государство
невероятно подумано, но это поможет проиллюстрировать точку. Теперь, чтобы обновить счет, нам нужно обновить недвижимость А
, который в свою очередь нуждается в обновлении B
И это будет содержать наш обновленный Считать
Отказ Функция обновления для Увеличение
Теперь нужно будет выглядеть так:
increase() { this.setState((state) => ({a: {...state.a, b: {...state.a.b, count: state.a.b.count + 1}} })) }
Это работает, но не очень читается. Давайте кратко посмотрим, что здесь происходит.
Существующее состояние передается в функцию, и мы хотим вернуть объект, который представляет объект, который будет объединен с Государство
Отказ SetState
Метод не сливается рекурсивно, так что делает что-то вроде this.setState ((состояние) = > ({a: {b: {count: counts.a.b.count + 1}}}))
будет обновлять счет, но другие свойства на А
и B
будет потерян. Чтобы предотвратить это, возвращенный объект создается путем распространения существующих свойств State.a
в новый объект, где мы тогда заменим B
Отказ С B
Также есть свойства, которые мы хотим сохранить, но не хотят меняться, мы распространяем State.b
реквизит и заменить только Считать
, который заменен новым значением, основанным на значении в State.a.b.count
Отказ
Конечно, нам нужно сделать то же самое с снижаться
Так что теперь весь компонент выглядит так:
import React from 'react'; import { render } from 'react-dom'; class App extends React.Component { constructor(props) { super(props) this.state = { a: { name: 'pointless structure', b : { stuff: 'things', count: 0 } } } this.increase = this.increase.bind(this) this.decrease = this.decrease.bind(this) } increase() { this.setState((state) => ({a: {...state.a, b: {...state.a.b, count: state.a.b.count + 1}} })) } decrease() { this.setState((state) => ({a: {...state.a, b: {...state.a.b, count: state.a.b.count - 1}} })) } render() { return () } } render({this.state.a.name}
{this.state.a.b.stuff}
{this.state.a.b.count}, document.getElementById('root'));
Те SetState
Звонки своего рода беспорядок! Хорошая новость, Есть лучший способ Отказ Линзы помогут нам очистить это и вернуться к обновлениям состояния, которые являются как читабельными, так и четко передают намерение обновления.
Линзы позволяют вам взять объект и «войти в него» или «сосредоточиться на конкретном свойстве этого объекта. Вы можете сделать это, указав путь, чтобы поставить свой фокус на свойство, которое глубоко вложено внутри объекта. С помощью этой линзы ориентированы на вашу цель, вы можете затем установить новые значения в этом свойстве, не теряя контекст окружающего объекта.
Создать объектив, который фокусируется на Считать
Недвижимость в нашем состоянии мы будем использовать Ramda Лансапат
Функция и массив, который описывает путь к Считать
, вот так:
import {lensPath} from 'ramda' const countLens = lensPath(['a', 'b', 'count'])
Теперь, когда у нас есть объектив, мы можем использовать его с одним из ограниченных объективных функций, доступных в Ramda: Вид
, задавать
и над
Отказ Если мы запустим Вид
Передача на нашу линз и объект штата, мы вернемся на ценность Считать
Отказ
import {lensPath, view} from 'ramda' const countLens = lensPath(['a', 'b', 'count']) // somewhere with access to the component's state view(countLens, state) // 0
По общему признанию, Вид
не кажется супер полезным, так как мы могли бы только ссылаться на путь к State.a.b.count
или используйте Ramda путь
функция. Давайте посмотрим, как мы можем сделать что-то полезное с нашей линзой. Для этого мы собираемся использовать Установить
функция.
import {lensPath, view, set} from 'ramda' const countLens = lensPath(['a', 'b', 'count']) // somewhere with access to the component's state const newValue = 20 set(countLens, newValue, state)
Когда мы сделаем это, мы вернемся к объекту, похожему на себя:
{ a: { name: 'pointless structure', b : { stuff: 'things', count: 20 // update in context } } }
Мы вернули новую версию нашего Государство
объект, в котором значение State.a.b.count
был заменен на 20
Отказ Таким образом, мы не только сделали целевое изменение глубоко в структуре объекта, мы сделали это неизбежно!
Так что, если мы возьмем то, что мы узнали до сих пор, мы можем обновить наше увеличение
Метод в нашем компоненте, чтобы найти больше всего подобного:
increase() { this.setState((state) => { const currentCount = view(countLens, state) return set(countLens, currentCount+1, state) }) }
Мы использовали Вид
с нашим объективом, чтобы получить текущее значение, а затем называется Установить
Чтобы обновить значение на основе старого значения и вернуть совершенно новую версию нашей Государство
Отказ
Мы можем сделать этот шаг дальше. над
Функция принимает объектив и функцию для применения к цели объектива. Результат функции затем назначается в качестве значения этой цели в возвращенном объекте. Итак, мы можем использовать Ramda Inc
функция для увеличения числа. Так что теперь мы можем сделать Увеличение
Метод выглядит как:
increase() { this.setState((state) => over(countLens, inc, state)) }
Довольно круто, верно?! Ну, это становится еще лучше … Нет, на самом деле, это делает!
Все функции Ramda автоматически связываются, поэтому, если мы пройдем над
Просто первый аргумент, мы вернем новую функцию, которая ожидает второго и третьего аргументов. Если я передаю его первые два аргумента, он возвращает функцию, которая ожидает последнего аргумента. Так что это означает, что я могу сделать это:
increase() { this.setState((state) => over(countLens, inc)(state)) }
Где первоначальный вызов над
Возвращает функцию, которая принимает Государство
Отказ Ну, SetState
принимает функцию, которая принимает Государство
Как аргумент, так что теперь я могу сократить все дело до:
increase() { this.setState(over(countLens, inc)) }
И если это не передает достаточно смысла для вас, вы можете переместить это над
функционируйте из компонента и придайте ему приятное значимое имя:
// outside of the component: const increaseCount = over(countLens, inc) // Back in the component increase() { this.setState(increaseCount) }
И, конечно же, то же самое можно сделать для уменьшить
Способ использования ДЕКС
от Рамды. Это заставит всю установку для этого компонента выглядеть так:
import React from 'react'; import { render } from 'react-dom'; import {inc, dec, lensPath, over} from 'ramda' const countLens = lensPath(['a', 'b', 'count']) const increaseCount = over(countLens, inc) const decreaseCount = over(countLens, dec) class App extends React.Component { constructor(props) { super(props) this.state = { a: { name: 'pointless structure', b : { stuff: 'things', count: 0 } } } this.increase = this.increase.bind(this) this.decrease = this.decrease.bind(this) } increase() { this.setState(increaseCount) } decrease() { this.setState(decreaseCount) } render() { return () } } render({this.state.a.name}
{this.state.a.b.stuff}
{this.state.a.b.count}, document.getElementById('root'));
Приятно здесь, если форма изменения состояния, мы можем обновить нашу государственную манипулировать логику, просто настроив Лансапат
Отказ На самом деле, мы могли бы даже использовать объектив вместе с Вид
Чтобы отобразить наши данные в оказывать
И тогда мы могли полагаться на это Лансапат
обрабатывать все наших ссылок на счет!
Так что это будет означать это: {the.state.a.b.count}
будет заменен был бы заменен результатом: Вид (Countlens, это .state)
в оказывать
метод.
Итак, вот с этой окончательной корректировкой, возьмите его для спина и посмотрите, что вы можете сделать с этим!
Оригинал: “https://dev.to/avanslaars/immutable-deep-state-updates-in-react-with-ramdajs”