it-roy-ru.com

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

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

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

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

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

17
Joshua

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

18
Alexander T.

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

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

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

12
Distortum

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

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

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

3
MiF

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

`` ` _processPendingState: function (props, context) { 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 или подобного, что означает, что это не совсем намеченное поведение. Кроме того, полное копирование статистики, удаление свойства и вызов setState не будут работать, поскольку setState всегда является слиянием, поэтому удаленное свойство будет просто игнорироваться. 

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

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

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

На самом деле вы можете добавить больше информации;

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

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

2
Steve Cooper

Вы можете использовать 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>

1
Vince

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

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

this.removekey('thekey');

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

0
Taj Virani

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

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

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

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

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

0
Malvineous