Javascript - Funções Pure vs Impure

12

Passei pelas definições dos dois como:

Funções puras são aquelas que não tentam alterar suas entradas e sempre retornam o mesmo resultado para as mesmas entradas.

Exemplo

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

E a função Impura é aquela que altera sua própria entrada.

Exemplo

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

Definições e snippets de código extraídos dos documentos oficiais do ReactJs .

Agora, alguém pode me dizer, como posso cometer alguns erros no React / Redux , para usar funções impuras onde são necessárias funções puras?

MuhammadUmarFarooq
fonte
4
Funções impuras têm efeitos colaterais. Algo como window.getElementByIdetc, portanto, executar a mesma função com os mesmos parâmetros pode ter resultados diferentes, dependendo dos efeitos colaterais. É aí que o redux falharia.
Dominik

Respostas:

10

Reagir e Reduxambos precisam de funções puras, aliadas à imutabilidade, para serem executadas de maneira previsível.

Se você não seguir essas duas coisas, seu aplicativo terá bugs, sendo o mais comum React/Reduxnão conseguir rastrear as alterações e não conseguir renderizá- las novamente novamente state/prop.

Em termos de React, considere o seguinte exemplo:

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();
}

O estado é mantido pelo objeto state, que apenas adicionou propriedade. Este aplicativo renderiza a propriedade do aplicativo. Ele nem sempre deve renderizar o estado quando algo acontece, mas deve verificar se ocorreu uma alteração no objeto de estado.

Assim, temos uma função de efeitos, pure functionque usamos para afetar nosso estado. Você vê que ele retorna um novo estado quando o estado deve ser alterado e retorna o mesmo estado quando nenhuma modificação é necessária.

Também temos uma shouldUpdatefunção que verifica, usando o operador ===, se o estado antigo e o novo estado são iguais.

Para cometer erros em termos de React, você pode realmente fazer o seguinte:

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;
}

Você também pode cometer erros definindo o estado diretamente e não usando a effectsfunção.

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

O procedimento acima não deve ser feito e apenas a effectsfunção deve ser usada para atualizar o estado.

Em termos de Reagir, nós chamamos effectscomo setState.

Para Redux:

  1. O combineReducersutilitário do Redux verifica alterações de referência.
  2. O connectmétodo React-Redux gera componentes que verificam as alterações de referência para o estado raiz e os valores de retorno das mapStatefunções para verificar se o componente empacotado realmente precisa ser renderizado novamente.
  3. A depuração da viagem no tempo exige que o redutor pure functionsnão tenha efeitos colaterais, para que você possa saltar corretamente entre diferentes estados.

Você pode violar facilmente os três acima usando funções impuras como redutores.

O seguinte é obtido diretamente dos documentos do redux:

É chamado de redutor porque é o tipo de função para a qual você passaria Array.prototype.reduce(reducer, ?initialValue).
É muito importante que o redutor permaneça puro. Coisas que você nunca deve fazer dentro de um redutor:

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

Dados os mesmos argumentos, ele deve calcular o próximo estado e retorná-lo. Sem surpresas. Sem efeitos colaterais. Nenhuma chamada de API. Sem mutações. Apenas um cálculo.

Utsav Patel
fonte
7

Simplesmente disse que o estado não pode ser modificado. Uma nova instância do estado deve ser retornada toda vez que houver uma alteração, para

Este código não está correto:

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
  }
}

Este código, quando escrito como uma função pura abaixo, retorna uma nova instância da matriz que não modifica a própria matriz real. Esta é a razão pela qual você deve usar uma biblioteca como o immer para lidar com a imutabilidade

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
  }
}
muddassir
fonte
5

Você pode tornar impuras as funções puras adicionando chamadas à API ou escrevendo códigos que resultam em efeitos colaterais.

As funções puras devem sempre ser objetivas e auto-explicativas, e não exigir que você indique 3 ou 4 outras funções para entender o que está acontecendo.

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

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

Em caso de 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
   }
  }
}

Isso não deve ser feito . Tudo o que uma função de conexão ou função redutora precisa ser fornecida por meio de argumento ou escrito em sua função. Nunca deve sair de fora.

Pushkin
fonte