Javascript - чистые против нечистых функций

12

Я прошел через определения двух как:

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

пример

function sum(a, b) {
  return a + b;
}

И нечистая функция это та, которая меняет свой собственный вход.

пример

function withdraw(account, amount) {
  account.total -= amount;
}

Определения и фрагменты кода взяты из официальных документов ReactJ .

Теперь, может кто-нибудь сказать мне, как я могу сделать некоторые ошибки в React / Redux , чтобы использовать нечистые функции там, где требуются чистые функции?

MuhammadUmarFarooq
источник
4
Нечистые функции имеют побочные эффекты. Нечто подобное и window.getElementByIdт.д., поэтому запуск одной и той же функции с одинаковыми параметрами может иметь разные результаты в зависимости от побочных эффектов. Который, где излишек потерпит неудачу.
Доминик

Ответы:

10

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

Если вы не выполните эти две вещи, ваше приложение будет иметь ошибки, наиболее распространенными из которых являются React/Reduxнеспособность отслеживать изменения и невозможность повторного рендеринга при ваших state/propизменениях.

С точки зрения React рассмотрим следующий пример:

let state = {
    add: 0,
}

function render() {
    //...
}
//pure function
function effects(state,action) {
//following immutability while updating state, not directly mutating the state.
    if(action == 'addTen') {
        return {...state, add: state.add + 10} 
    }
    return state;
}

function shouldUpdate(s) {
    if(s === state){
        return false
    }
    return true
}

state = effects(state, 'addTen')if(shouldUpdate(state)) {
    render();
}

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

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

У нас также есть shouldUpdateфункция, которая с помощью оператора === проверяет, совпадают ли старое и новое состояние.

Чтобы сделать ошибки с точки зрения React, вы можете сделать следующее:

function effects(state,action) {

  doRandom(); // effects should only be called for updating state.
             // Doing any other stuff here would make effects impure.

    if(action == 'addTen') {
        return {...state, add: state.add + 10}
    }
    return state;
}

Вы также можете делать ошибки, устанавливая состояние напрямую и не используя effectsфункцию.

function doMistake(newValue) {
    this.state = newValue
}

Выше не должно быть сделано, и только effectsфункция должна использоваться, чтобы обновить состояние.

С точки зрения React, мы называем effectsкак setState.

Для Redux:

  1. combineReducersУтилита Redux проверяет изменения ссылок.
  2. connectМетод React-Redux генерирует компоненты, которые проверяют изменения ссылок как для корневого состояния, так и для возвращаемых значений из mapStateфункций, чтобы увидеть, действительно ли обернутый компонент должен повторно визуализироваться.
  3. Отладка во времени требует, чтобы редуктор не pure functionsимел побочных эффектов, чтобы вы могли правильно переключаться между различными состояниями.

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

Следующее взято непосредственно из документов Redx:

Это называется редуктором, потому что это тип функции, который вы передадите Array.prototype.reduce(reducer, ?initialValue).
Очень важно, чтобы редуктор оставался чистым. То, что вы никогда не должны делать внутри редуктора:

Mutate its arguments;
Perform side effects like API calls and routing transitions;
Call non-pure functions, e.g. Date.now() or Math.random().

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

Утсав Патель
источник
7

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

Этот код не является правильным:

const initialStates = {    
  items: ['item1']
}

export const ItemMaster = (state = initialStates, action) => {    
  switch (action.type) {
    case TYPES.ADD_ITEM:            
    {
        state.items.push(action.item)
        return state
    }
    default:
      return state
  }
}

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

const initialStates = { 
  items: ['item1']
}

export const ItemMaster = (state = initialStates, action) => {    
  switch (action.type) {
    case TYPES.ADD_ITEM:            
    {

        state = {...state,items:state.items.concat(action.item)}
        return state
    }
    default:
      return state
  }
}
Мудассир
источник
5

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

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

// Pure Function
function USDtoEUR(USD, todayRate) {
  return USD * todayRate;
}

// Impure Function 
function USDtoEUR(USD) {
  const todayRate = getTodayRate();
  return USD * todayRate;
}

В случае React / Redux

const mapState = async state => {
  const { data } = await whatDoINeed()

  let mappedState = {}

  if (data.needDolphin) {
    mappedState.dolphin = state.dolphin
  }

  if (data.needShark) {
    mappedState.shark= state.shark
  }

  return mappedState;
}

// Or for Redux Reducer
// Bad
{
  setData: (state, payload) => {
   const set = whatToSet()
   return {
     ...state,
     set.dolphin ? ...{ dolphin: payload.dolphin } : ...{},
     set.shark ? ...{ shark : payload.shark } : ...{},
   }
  }
}

// Good
{
  setData: (state, payload) => {
   return {
     ...state,
     // Just send only the things need
     // to be sent
     ...payload
   }
  }
}

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

Пушкин
источник