Javascript - Funzioni pure vs impure


12

Ho esaminato le definizioni dei due come:

Le funzioni pure sono quelle che non tentano di modificare i propri input e restituiscono sempre lo stesso risultato per gli stessi input.

Esempio

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

E la funzione Impure è quella che cambia il proprio input.

Esempio

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

Definizioni e frammenti di codice tratti dai documenti ufficiali di ReactJs .

Ora, qualcuno può dirmi, come posso fare alcuni errori in React / Redux , per usare le funzioni impure dove sono richieste funzioni pure?


4
Le funzioni impure hanno effetti collaterali. Qualcosa di simile window.getElementByIdecc., Quindi eseguire la stessa funzione con gli stessi parametri può avere risultati diversi a seconda degli effetti collaterali. È qui che il redux fallirebbe.
Dominik

Risposte:


10

Reagire ed Reduxentrambi hanno bisogno di funzioni pure abbinate a immutabilità per funzionare in modo prevedibile.

Se non segui queste due cose, la tua app avrà dei bug, il più comune React/Reduxnon è in grado di tracciare le modifiche e non è in grado di eseguire nuovamente il rendering quando le state/propmodifiche.

In termini di React, considera il seguente esempio:

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

Lo stato è trattenuto dall'oggetto state che ha solo aggiunto proprietà. Questa app esegue il rendering della proprietà dell'app. Non dovrebbe sempre rendere lo stato quando succede qualcosa, ma dovrebbe verificare se si è verificato un cambiamento nell'oggetto stato.

Allo stesso modo, abbiamo una funzione di effetti, pure functionche usiamo per influenzare il nostro stato. Si vede che restituisce un nuovo stato quando lo stato deve essere modificato e restituisce lo stesso stato quando non è richiesta alcuna modifica.

Abbiamo anche una shouldUpdatefunzione che controlla usando l'operatore === se il vecchio stato e il nuovo stato sono uguali.

Per fare errori in termini di React, puoi effettivamente fare quanto segue:

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

Puoi anche fare errori impostando direttamente lo stato e non usando la effectsfunzione.

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

Quanto sopra non dovrebbe essere fatto e solo la effectsfunzione dovrebbe essere usata per aggiornare lo stato.

In termini di React, chiamiamo effectsas setState.

Per Redux:

  1. L' combineReducersutilità di Redux verifica le modifiche di riferimento.
  2. Il connectmetodo di React-Redux genera componenti che controllano le modifiche di riferimento sia per lo stato radice che per i valori di ritorno dalle mapStatefunzioni per vedere se il componente spostato deve effettivamente eseguire nuovamente il rendering.
  3. Il debug dei viaggi nel tempo richiede che il riduttore pure functionsnon abbia effetti collaterali in modo da poter saltare correttamente tra stati diversi.

Puoi facilmente violare i tre precedenti usando funzioni impure come riduttori.

Quello che segue è preso direttamente dai documenti redux:

Si chiama riduttore perché è il tipo di funzione a cui si passerebbe Array.prototype.reduce(reducer, ?initialValue).
È molto importante che il riduttore rimanga puro. Cose che non dovresti mai fare all'interno di un riduttore:

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

Dati gli stessi argomenti, dovrebbe calcolare lo stato successivo e restituirlo. Niente sorprese. Nessun effetto collaterale Nessuna chiamata API. Nessuna mutazione. Solo un calcolo.


7

Detto semplicemente che lo stato non può essere mutato. Una nuova istanza dello stato deve essere restituita ogni volta che si verifica una modifica

Questo codice non è corretto:

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

Questo codice, se scritto come una pura funzione di seguito, restituisce una nuova istanza dell'array e non modifica l'array vero e proprio. Questo è il motivo per cui dovresti usare una libreria come immer per gestire l'immutabilità

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

È possibile rendere impure funzioni pure aggiungendo chiamate API o scrivendo codici che generano effetti collaterali.

Le funzioni pure dovrebbero sempre essere mirate e autoesplicative, e non dovrebbero richiedere di fare riferimento a 3 o 4 altre funzioni per capire cosa sta succedendo.

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

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

In caso di 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
   }
  }
}

Questo non dovrebbe essere fatto . Tutto ciò di cui ha bisogno una funzione di connessione o di riduzione deve essere fornito tramite argomento o scritto all'interno della sua funzione. Non dovrebbe mai arrivare dall'esterno.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.