web-dev-qa-db-fra.com

Comment puis-je supprimer un attribut d'un objet d'état d'un composant React

Si j'ai un composant de réaction ayant une propriété définie sur son état:

onClick() {
    this.setState({ foo: 'bar' });
}

Est-il possible de supprimer "foo" ici de Object.keys(this.state)?

La méthode replaceState ressemble à la méthode évidente à essayer, mais elle a été dépréciée depuis.

17
Joshua

Vous pouvez définir foo sur undefined, comme suit 

var Hello = React.createClass({
    getInitialState: function () {
        return {
            foo: 10,
            bar: 10
        }
    },

    handleClick: function () {
        this.setState({ foo: undefined });
    },

    render: function() {
        return (
            <div>
                <div onClick={ this.handleClick.bind(this) }>Remove foo</div>
                <div>Foo { this.state.foo }</div>
                <div>Bar { this.state.bar }</div>
            </div>
        );
    }
});

Example

Aussi, vous pouvez utiliser delete

delete this.state.foo;
this.setState(this.state);

mais ceci est dangereux car cette solution manipule directement this.state

Mettre à jour

La solution précédente supprime simplement la valeur de foo et key et la compétence existe dans state, si vous devez supprimer complètement la clé de state, l'une des solutions possibles peut être setState avec un parent key, comme ceci

var Hello = React.createClass({
  getInitialState: function () {
    return {
      data: {
        foo: 10,
        bar: 10
      }
    }
  },
    	
  handleClick: function () {
    const state = {
      data: _.omit(this.state.data, 'foo')
    };
    
    this.setState(state, () => {
      console.log(this.state);
    });
  },
        
  render: function() {
    return (
      <div>
        <div onClick={ this.handleClick }>Remove foo</div>
        <div>Foo { this.state.data.foo }</div>
        <div>Bar { this.state.data.bar }</div>
      </div>
    );
  }
});

ReactDOM.render(<Hello />, document.getElementById('container'))
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container"></div>

18
Alexander T.

Le code suivant supprimera foo de this.state sans modifier directement this.state:

const {foo, ...state} = this.state;
this.setState({state});

Vous aurez besoin du préréglage stage 3 pour que cela fonctionne.

12
Distortum

Solution précédente - est anti-modèle, parce que cela change cet état. Il est faux!

Utilisez ceci (à l'ancienne):

let newState = Object.assign({}, this.state) // Copy state
newState.foo = null // modyfy copyed object, not original state
// newState.foo = undefined // works too
// delete newState.foo // Wrong, do not do this
this.setState(newState) // set new state

Ou utilisez du sucre ES6:

this.setState({...o, a:undefined})

Assez gentil, n'est-ce pas? ))

Dans l'ancienne syntaxe de React (originale, pas ES6), cela a this.replaceState, qui supprime les clés inutiles en magasin, mais maintenant c'est deprecated

3
MiF

Dans ReactCompositeComponent.js, dans la source de React sur GitHub, il existe une méthode appelée _processPendingState, qui est la méthode ultime qui implémente l'état de fusion à partir d'appels de composant.setState;

`` `. _processPendingState: function (props, context) { var inst = this._instance; file d'attente var = this._pendingStateQueue; var replace = this._pendingReplaceState; this._pendingReplaceState = false; this._pendingStateQueue = null;

if (!queue) {
  return inst.state;
}

if (replace && queue.length === 1) {
  return queue[0];
}

var nextState = replace ? queue[0] : inst.state;
var dontMutate = true;
for (var i = replace ? 1 : 0; i < queue.length; i++) {
  var partial = queue[i];
  let partialState = typeof partial === 'function'
    ? partial.call(inst, nextState, props, context)
    : partial;
  if (partialState) {
    if (dontMutate) {
      dontMutate = false;
      nextState = Object.assign({}, nextState, partialState);
    } else {
      Object.assign(nextState, partialState);
    }
  }
}

`` `

Dans ce code, vous pouvez voir la ligne qui implémente la fusion;

nextState = Object.assign({}, nextState, partialState);

Nulle part dans cette fonction, il n’ya un appel à delete ou similaire, ce qui signifie que ce comportement n’est pas vraiment prévu. De plus, copier complètement la statistique, supprimer la propriété et appeler setState ne fonctionneront pas, car setState est toujours une fusion. Par conséquent, la propriété supprimée sera simplement ignorée. 

Notez également que setState ne fonctionne pas immédiatement, mais que les lots changent. Par conséquent, si vous essayez de cloner tout l'objet state et de ne modifier que les propriétés, vous pouvez effacer les appels précédents à setState. Comme le dit le document React;

React peut regrouper plusieurs appels setState () dans une seule mise à jour pour améliorer les performances.

Étant donné que this.props et this.state peuvent être mis à jour de manière asynchrone, vous ne devez pas compter sur leurs valeurs pour calculer l'état suivant.

Ce que vous pourriez chercher à faire est d’ajouter plus d’informations;

this.setState({ xSet: true, x: 'foo' });
this.setState({ xSet: false, x: undefined });

C’est moche, certes, mais cela vous donne l’information supplémentaire dont vous avez besoin pour différencier une valeur définie comme indéfinie d’une valeur non définie. De plus, il joue à Nice avec les composants internes de React, ses transactions, le traitement en lots des changements d’état et toute autre horreur. Mieux vaut prendre un peu plus de complexité ici que d'essayer de deviner les composants internes de Reacts, qui regorgent d'horreurs telles que la réconciliation des transactions, la gestion de fonctionnalités obsolètes telles que replaceState, etc.

2
Steve Cooper

Vous pouvez utiliser Object.assign pour créer une copie superficielle de l'état de votre application à la profondeur appropriée et supprimer l'élément de votre copie. Ensuite, utilisez setState pour fusionner votre copie modifiée dans l'état de l'application.

Ce n'est pas une solution parfaite. Copier un objet entier comme celui-ci peut entraîner des problèmes de performances/mémoire. La copie superficielle de Object.assign permet d'atténuer les problèmes de mémoire/performances, mais vous devez également savoir quelles parties de votre nouvel objet sont des copies et quelles sont les références aux données dans l'état de l'application.

Dans l'exemple ci-dessous, la modification du tableau ingredients modifierait en réalité directement l'état de l'application.

Définir la valeur de l'élément indésirable sur null ou undefined ne la supprime pas.

const Component = React.Component;

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      "recipes": {
        "1": {
          "id": 1,
          "name": "Pumpkin Pie",
          "ingredients": [
            "Pumpkin Puree",
            "Sweetened Condensed Milk",
            "Eggs",
            "Pumpkin Pie Spice",
            "Pie Crust"
          ]
        },
        "2": {
          "id": 2,
          "name": "Spaghetti",
          "ingredients": [
            "Noodles",
            "Tomato Sauce",
            "(Optional) Meatballs"
          ]
        },
        "3": {
          "id": 3,
          "name": "Onion Pie",
          "ingredients": [
            "Onion",
            "Pie Crust",
            "Chicken Soup Stock"
          ]
        },
        "4": {
          "id": 4,
          "name": "Chicken Noodle Soup",
          "ingredients": [
            "Chicken",
            "Noodles",
            "Chicken Stock"
          ]
        }
      },
      "activeRecipe": "4",
      "warningAction": {
        "name": "Delete Chicken Noodle Soup",
        "description": "delete the recipe for Chicken Noodle Soup"
      }
    };
    
    this.renderRecipes = this.renderRecipes.bind(this);
    this.deleteRecipe = this.deleteRecipe.bind(this);
  }
  
  deleteRecipe(e) {
    const recipes = Object.assign({}, this.state.recipes);
    const id = e.currentTarget.dataset.id;
    delete recipes[id];
    this.setState({ recipes });
  }
  
  renderRecipes() {
    const recipes = [];
    for (const id in this.state.recipes) {
      recipes.Push((
        <tr>
          <td>
            <button type="button" data-id={id} onClick={this.deleteRecipe}
            >&times;</button>
          </td>
          <td>{this.state.recipes[id].name}</td>
        </tr>
      ));
    }
    return recipes;
  }
                
  render() {
    return (
      <table>
        {this.renderRecipes()}
      </table>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<main id="app"></main>

1
Vince

Si la suppression est dans une fonction et que la clé doit être une variable, essayez ceci:

removekey = (keyname) => {
    let newState = this.state;
    delete newState[keyname];
    this.setState({ newState })
}

this.removekey('thekey');

Presque la même chose que la réponse de steve, mais dans une fonction.

0
Taj Virani

Je pense que c'est une bonne façon de s'y prendre =>

//in constructor
let state = { 
   sampleObject: {0: a, 1: b, 2: c }
}

//method
removeObjectFromState = (objectKey) => {
   let reducedObject = {}
   Object.keys(this.state.sampleObject).map((key) => {
      if(key !== objectKey) reducedObject[key] = this.state.sampleObject[key];
   })
   this.setState({ sampleObject: reducedObject });
}
0
Robi Moller

Si vous souhaitez réinitialiser complètement l'état (en supprimant un grand nombre d'éléments), ceci fonctionne comme suit:

this.setState(prevState => {
    let newState = {};
    Object.keys(prevState).forEach(k => {
        newState[k] = undefined;
    });
    return newState;
});

L’utilisation de cette variante de setState vous permet d’avoir accès à l’ensemble de l’état pendant l’appel, alors que this.state pourrait être un peu obsolète (en raison des précédents appels setState n'ayant pas encore été traités).

0
Malvineous