Javascript - Fonctions Pure vs Impure

12

J'ai parcouru les définitions des deux comme:

Les fonctions pures sont celles qui n'essaient pas de modifier leurs entrées et retournent toujours le même résultat pour les mêmes entrées.

Exemple

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

Et la fonction Impure est celle qui change sa propre entrée.

Exemple

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

Définitions et extraits de code extraits des documents officiels de ReactJs .

Maintenant, quelqu'un peut-il me dire, comment puis-je faire des erreurs dans React / Redux , pour utiliser des fonctions impures où des fonctions pures sont requises?

MuhammadUmarFarooq
la source
4
Les fonctions impures ont des effets secondaires. Quelque chose comme window.getElementByIdetc donc exécuter la même fonction avec les mêmes paramètres peut avoir des résultats différents selon les effets secondaires. C'est là que redux échouerait.
Dominik

Réponses:

10

React et les Reduxdeux ont besoin de fonctions pures couplées à une immuabilité pour fonctionner de manière prévisible.

Si vous ne suivez pas ces deux choses, votre application aura des bogues, le plus courant étant React/Reduxincapable de suivre les modifications et de ne pas pouvoir effectuer un nouveau rendu lorsque vos state/propmodifications.

En termes de React, considérons l'exemple suivant:

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

L'état est détenu par l'objet état qui n'a ajouté que des propriétés. Cette application rend la propriété de l'application. Il ne doit pas toujours rendre l'état lorsque quelque chose se produit, mais doit vérifier si un changement s'est produit dans l'objet d'état.

Comme cela, nous avons une fonction d'effets, pure functionque nous utilisons pour affecter notre état. Vous voyez qu'il renvoie un nouvel état lorsque l'état doit être modifié et renvoie le même état lorsqu'aucune modification n'est requise.

Nous avons également une shouldUpdatefonction qui vérifie à l'aide de l'opérateur === si l'ancien état et le nouvel état sont identiques.

Pour faire des erreurs en termes de React, vous pouvez en fait procéder comme suit:

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

Vous pouvez également faire des erreurs en définissant l'état directement et en n'utilisant pas de effectsfonction.

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

Ce qui précède ne doit pas être fait et seule la effectsfonction doit être utilisée pour mettre à jour l'état.

En termes de React, nous appelons effectsas setState.

Pour Redux:

  1. L' combineReducersutilitaire de Redux vérifie les changements de référence.
  2. La connectméthode de React-Redux génère des composants qui vérifient les changements de référence pour l'état racine et les valeurs de retour des mapStatefonctions pour voir si le composant encapsulé doit réellement être rendu de nouveau.
  3. Le débogage dans le temps nécessite que le réducteur soit pure functionssans effets secondaires afin que vous puissiez passer correctement entre les différents états.

Vous pouvez facilement violer les trois ci-dessus en utilisant des fonctions impures comme réducteurs.

Ce qui suit est tiré directement des documents redux:

C'est ce qu'on appelle un réducteur, car c'est le type de fonction que vous transmettez Array.prototype.reduce(reducer, ?initialValue).
Il est très important que le réducteur reste pur. Choses que vous ne devriez jamais faire à l'intérieur d'un réducteur:

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

Compte tenu des mêmes arguments, il doit calculer l'état suivant et le renvoyer. Pas de surprises. Pas d'effets secondaires. Aucun appel API. Pas de mutations. Juste un calcul.

Utsav Patel
la source
7

Dit simplement que l'État ne peut pas être muté. Une nouvelle instance de l'état doit être retournée chaque fois qu'il y a un changement afin

Ce code n'est pas correct:

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

Ce code lorsqu'il est écrit comme une fonction pure ci-dessous, cela renvoie une nouvelle instance du tableau, il ne modifie pas le tableau lui-même. C'est la raison pour laquelle vous devez utiliser une bibliothèque comme immer pour gérer l'immuabilité

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
la source
5

Vous pouvez rendre les fonctions pures impures en ajoutant des appels API ou en écrivant des codes entraînant des effets secondaires.

Les fonctions pures doivent toujours être pertinentes et explicites, et ne doivent pas vous obliger à consulter 3 ou 4 autres fonctions pour comprendre ce qui se passe.

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

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

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

Cela ne devrait pas être fait . Tout ce dont une fonction de connexion ou une fonction de réduction a besoin doit être fourni par argument ou écrit dans sa fonction. Elle ne devrait jamais sortir de l'extérieur.

Pouchkine
la source