Javascript - Funciones puras vs impuras

12

He revisado las definiciones de los dos como:

Las funciones puras son aquellas que no intentan cambiar sus entradas y siempre devuelven el mismo resultado para las mismas entradas.

Ejemplo

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

Y la función Impura es aquella que cambia su propia entrada.

Ejemplo

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

Definiciones y fragmentos de código tomados de los documentos oficiales de ReactJs .

Ahora, ¿alguien puede decirme, cómo puedo cometer algunos errores en React / Redux , para usar funciones impuras donde se requieren funciones puras?

MuhammadUmarFarooq
fuente
44
Las funciones impuras tienen efectos secundarios. Algo así como window.getElementByIdetc., por lo que ejecutar la misma función con los mismos parámetros puede tener resultados diferentes dependiendo de los efectos secundarios. Que es donde fallaría redux.
Dominik

Respuestas:

10

Reaccionan y Reduxambos necesitan funciones puras junto con la inmutabilidad para ejecutarse de manera predecible.

Si no sigue estas dos cosas, su aplicación tendrá errores, el más común React/Reduxno podrá rastrear los cambios y no podrá volver a renderizarlos state/prop.

En términos de React, considere el siguiente ejemplo:

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

El estado lo retiene el objeto de estado que solo ha agregado propiedad. Esta aplicación representa la propiedad de la aplicación. No siempre debe representar el estado cuando sucede algo, pero debe verificar si se produjo un cambio en el objeto de estado.

De esta manera, tenemos una función de efectos, pure functionque usamos para afectar nuestro estado. Verá que devuelve un nuevo estado cuando se va a cambiar el estado y devuelve el mismo estado cuando no se requiere ninguna modificación.

También tenemos una shouldUpdatefunción que verifica con el operador === si el estado anterior y el nuevo son los mismos.

Para cometer errores en términos de React, puedes hacer lo siguiente:

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

También puede cometer errores configurando el estado directamente y no utilizando la effectsfunción.

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

Lo anterior no se debe hacer y solo se effectsdebe usar la función para actualizar el estado.

En términos de React, llamamos effectscomo setState.

Para Redux:

  1. La combineReducersutilidad de Redux busca cambios de referencia.
  2. El connectmétodo React-Redux genera componentes que verifican los cambios de referencia tanto para el estado raíz como para los valores de retorno de las mapStatefunciones para ver si el componente envuelto realmente necesita volver a renderizarse.
  3. La depuración del viaje en el tiempo requiere que el reductor no tenga pure functionsefectos secundarios para que pueda saltar correctamente entre diferentes estados.

Puede violar fácilmente los tres anteriores utilizando funciones impuras como reductores.

Lo siguiente se toma directamente de los documentos de redux:

Se llama reductor porque es el tipo de función a la que pasaría Array.prototype.reduce(reducer, ?initialValue).
Es muy importante que el reductor se mantenga puro. Cosas que nunca debes hacer dentro de un reductor:

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

Dados los mismos argumentos, debe calcular el siguiente estado y devolverlo. No hay sorpresas. Sin efectos secundarios. No hay llamadas de API. Sin mutaciones Solo un calculo.

Utsav Patel
fuente
7

Simplemente dijo que el estado no puede ser mutado. Se debe devolver una nueva instancia del estado cada vez que haya un cambio.

Este código no es correcto:

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, cuando se escribe como una función pura a continuación, devuelve una nueva instancia de la matriz, no modifica la matriz real en sí. Esta es la razón por la que debería usar una biblioteca como immer para manejar la inmutabilidad

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
fuente
5

Puede hacer impuras las funciones puras agregando llamadas API o escribiendo códigos que provoquen efectos secundarios.

Las funciones puras siempre deben ser puntuales y se explican por sí mismas, y no deben requerir que refieras otras 3 o 4 funciones para comprender lo que está sucediendo.

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

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

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

Esto no debe hacerse . Todo lo que necesita una función de conexión o una función reductora debe ser suministrado a través de argumentos o escrito dentro de su función. Nunca debería llegar desde afuera.

Pushkin
fuente