Как удалить атрибут из объекта состояния компонента Reactjs

Если у меня есть компонент react, который имеет свойство, установленное в его состоянии:

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

можно ли удалить "foo" здесь Object.keys(this.state)?

на replaceState метод выглядит как очевидный способ попробовать, но с тех пор он обесценился.

7 ответов


вы можете установить foo до undefined вот так

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

также вы можете использовать delete,

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

но это опасно, потому что это решение напрямую манипулирует этим.государство

обновление

предыдущее решение просто удалить значение из foo и key искусство существует в state, если вам нужно полностью удалить ключ из state, одно из возможным решением может быть setState С одним из родителей key вот так

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>

следующий код удалит foo С this.state без мутирующими this.state:

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

вам понадобится Этап 3 предустановленных для этого, чтобы работать.


предыдущее решение-это антипаттерн, потому что оно меняет это.государство. Это неправильно!

используйте это (старый способ):

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

или используйте сахар ES6:

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

довольно мило, не так ли? ))

в старом синтаксисе React (оригинал, а не ES6) это имеет this.replaceState, которые удаляют ненужные ключи в магазине, но теперь это устаревший


на ReactCompositeComponent.js в источнике React на GitHub есть метод под названием _processPendingState, который является конечным методом, который реализует состояние слияния от вызовов до компонента.выполнении функция setState;

`` _processPendingState: функция (реквизит, контекст) { var inst = это._экземпляр; var queue = это._pendingStateQueue; var replace = это._pendingReplaceState; этот._pendingReplaceState = false; этот._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);
    }
  }
}

``

в этом код вы можете увидеть фактическую строку, которая реализует слияние;

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

нигде в этой функции есть вызов delete или подобное, что означает, что это не совсем намеренное поведение. Кроме того, полное копирование статистики, удаление свойства и вызов setState не будут работать, потому что setState всегда является слиянием, поэтому удаленное свойство будет просто проигнорировано.

обратите внимание также, что setState не работает сразу, но пакеты меняются, поэтому, если вы попытаетесь клонировать весь состояние объекта и только одно изменение свойства, вы можете стереть предыдущие вызовы setState. Как говорится в документе React;

React может пакетировать несколько вызовов setState () в одно обновление для производительности.

потому что это.реквизит и это.состояние может обновляться асинхронно, не следует полагаться на их значения для вычисления следующего состояния.

то, что вы могли бы посмотреть, чтобы сделать на самом деле добавить больше информации;

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

этот некрасиво, согласен, но это дает вам дополнительную часть информации нужно различать значение не определено, а значение не задано вообще. Кроме того, он хорошо играет с внутренними процессами React, транзакциями, пакетами изменений состояния и любым другим ужасом. Лучше взять немного дополнительной сложности здесь, чем пытаться предугадать внутренние реакции, которые полны ужасов, таких как согласование транзакций, управление устаревшими функциями, такими как replaceState и т. д.


можно использовать Object.assign чтобы сделать мелкую копию состояния вашего приложения на правильной глубине и удалить элемент из копии. Тогда используйте setState чтобы объединить измененную копию обратно в состояние приложения.

это не идеальное решение. Копирование всего объекта может привести к проблемам производительности / памяти. Object.assignмелкая копия помогает облегчить проблемы с памятью / производительностью, но вам также нужно знать какие части нового объекта являются копиями, а какие-ссылками на данные в состоянии приложения.

в приведенном ниже примере изменение ingredients array фактически изменит состояние приложения напрямую.

установка значения нежелательного элемента в null или undefined не удаляет его.

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>

Если удаление находится в функции, и ключ должен быть переменной, попробуйте следующее:

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

this.removekey('thekey');

почти то же самое, что и ответ Стива, но в функции.


если вы хотите полностью сбросить состояние (удаление большого количества элементов), что-то вроде этого работает:

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

используя этот вариант setState позволяет получить доступ ко всему состоянию во время вызова, тогда как this.state может быть немного устаревшим (из-за предыдущего setState звонки еще не были полностью обработаны).