Javascript - Reine vs unreine Funktionen


12

Ich habe die Definitionen der beiden wie folgt durchgesehen:

Reine Funktionen sind solche, die nicht versuchen, ihre Eingaben zu ändern, und immer dasselbe Ergebnis für dieselben Eingaben zurückgeben.

Beispiel

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

Und die unreine Funktion ändert ihre eigene Eingabe.

Beispiel

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

Definitionen und Code-Schnipsel aus den offiziellen Dokumenten von ReactJ .

Kann mir jetzt jemand sagen, wie ich in React / Redux einige Fehler machen kann , um unreine Funktionen zu verwenden, bei denen reine Funktionen erforderlich sind?


4
Unreine Funktionen haben Nebenwirkungen. So etwas wie window.getElementByIdetc, also kann das Ausführen derselben Funktion mit denselben Parametern je nach Nebenwirkungen zu unterschiedlichen Ergebnissen führen. Hier würde Redux scheitern.
Dominik

Antworten:


10

Reagieren und Reduxbeide benötigen reine Funktionen gepaart mit Unveränderlichkeit, um vorhersehbar zu laufen.

Wenn Sie diese beiden Dinge nicht befolgen, weist Ihre App Fehler auf. Die häufigste ist, dass React/ReduxÄnderungen nicht nachverfolgt und bei state/propÄnderungen nicht erneut gerendert werden können .

Betrachten Sie in Bezug auf React das folgende Beispiel:

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

Der Status wird von dem Statusobjekt gehalten, das nur eine Eigenschaft hinzugefügt hat. Diese App rendert die App-Eigenschaft. Es sollte nicht immer den Status rendern, wenn etwas passiert, sondern prüfen, ob eine Änderung im Statusobjekt aufgetreten ist.

So haben wir eine Effektfunktion, mit pure functionder wir unseren Zustand beeinflussen. Sie sehen, dass es einen neuen Status zurückgibt, wenn der Status geändert werden soll, und denselben Status zurückgibt, wenn keine Änderung erforderlich ist.

Wir haben auch eine shouldUpdateFunktion, die mit dem Operator === prüft, ob der alte und der neue Zustand gleich sind.

Um Fehler in Bezug auf React zu machen, können Sie tatsächlich Folgendes tun:

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

Sie können auch Fehler machen, indem Sie den Status direkt festlegen und die effectsFunktion nicht verwenden .

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

Dies sollte nicht durchgeführt werden und es sollte nur eine effectsFunktion zum Aktualisieren des Status verwendet werden.

In Bezug auf React nennen wir effectsas setState.

Für Redux:

  1. Das combineReducersDienstprogramm von Redux sucht nach Referenzänderungen.
  2. Die connectMethode von React-Redux generiert Komponenten, die Referenzänderungen sowohl für den Stammstatus als auch für die Rückgabewerte von mapStateFunktionen überprüfen, um festzustellen , ob die umschlossene Komponente tatsächlich neu gerendert werden muss.
  3. Das Debuggen von Zeitreisen erfordert, dass der Reduzierer pure functionskeine Nebenwirkungen aufweist, damit Sie korrekt zwischen verschiedenen Zuständen wechseln können.

Sie können die oben genannten drei leicht verletzen, indem Sie unreine Funktionen als Reduzierstücke verwenden.

Folgendes wird direkt aus Redux-Dokumenten entnommen:

Es wird als Reduzierer bezeichnet, da dies die Art von Funktion ist, an die Sie übergeben würden Array.prototype.reduce(reducer, ?initialValue).
Es ist sehr wichtig, dass der Reduzierer rein bleibt. Dinge, die Sie niemals in einem Reduzierer tun sollten:

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

Bei gleichen Argumenten sollte der nächste Status berechnet und zurückgegeben werden. Keine Überraschungen. Keine Nebenwirkungen. Keine API-Aufrufe. Keine Mutationen. Nur eine Berechnung.


7

Einfach gesagt, der Staat kann nicht mutiert werden. Bei jeder Änderung sollte eine neue Instanz des Status zurückgegeben werden

Dieser Code ist nicht korrekt:

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

Wenn dieser Code unten als reine Funktion geschrieben wird, wird eine neue Instanz des Arrays zurückgegeben, die das eigentliche Array selbst nicht ändert. Aus diesem Grund sollten Sie eine Bibliothek wie immer verwenden , um mit Unveränderlichkeit umzugehen

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

Sie könnten reine Funktionen unrein machen, indem Sie API-Aufrufe hinzufügen oder Codes schreiben, die zu Nebenwirkungen führen.

Reine Funktionen sollten immer auf den Punkt gebracht und selbsterklärend sein und nicht erfordern, dass Sie 3 oder 4 andere Funktionen referenzieren, um zu verstehen, was los ist.

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

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

Im Falle von 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
   }
  }
}

Dies sollte nicht getan werden . Alles, was eine Verbindungsfunktion oder Reduzierungsfunktion benötigt, muss durch Argument geliefert oder in ihre Funktion geschrieben werden. Es sollte niemals von außen kommen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.