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

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

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

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

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

Ответ 1

Вы можете установить 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);

но это опасно, потому что это решение напрямую манипулирует 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>

Ответ 2

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

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

Для этого вам понадобится предустановка этапа 3.

Ответ 3

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

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

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, который удаляет ненужные ключи в хранилище, но теперь он устарел

Ответ 4

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

`` ` _processPendingState: функция (реквизит, контекст) {   var inst = this._instance;   var queue = 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);
    }
  }
}

`` `

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

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

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

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

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

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

Что бы вы могли сделать, это на самом деле добавить дополнительную информацию,

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

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

Ответ 5

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

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

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

Установка значения нежелательного элемента на 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>

Ответ 6

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

removekey = (keyname) => {
    let newState = this.state;
    delete newState[keyname];
    this.setState(newState)
    // do not wrap the newState in additional curly braces  
}

this.removekey('thekey');

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

Ответ 7

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

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

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

Ответ 8

Я думаю, что это хороший способ сделать это =>

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

Ответ 9

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

handleClick: function () {
    let state = {...this.state};
    delete state.foo;
    this.setState(state);
},

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

});