Рубрики
Без рубрики

Неизменные глубокие государственные обновления в реакции с Ramda.js

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

Обновления базовых состояний в реакции представляют собой ветерок с использованием 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 (
      
{this.state.count}
) } } render(, 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 (
      

{this.state.a.name}

{this.state.a.b.stuff}

{this.state.a.b.count}
) } } render(, 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 (
      

{this.state.a.name}

{this.state.a.b.stuff}

{this.state.a.b.count}
) } } render(, document.getElementById('root'));

Приятно здесь, если форма изменения состояния, мы можем обновить нашу государственную манипулировать логику, просто настроив Лансапат Отказ На самом деле, мы могли бы даже использовать объектив вместе с Вид Чтобы отобразить наши данные в оказывать И тогда мы могли полагаться на это Лансапат обрабатывать все наших ссылок на счет!

Так что это будет означать это: {the.state.a.b.count} будет заменен был бы заменен результатом: Вид (Countlens, это .state) в оказывать метод.

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

Оригинал: “https://dev.to/avanslaars/immutable-deep-state-updates-in-react-with-ramdajs”