如果我有一个在其状态上设置了属性的 React 组件:
onClick() {
this.setState({ foo: 'bar' });
}
可以从"foo"
这里删除Object.keys(this.state)
吗?
该replaceState方法看起来像明显的方法来尝试,但它是不被提倡。
如果我有一个在其状态上设置了属性的 React 组件:
onClick() {
this.setState({ foo: 'bar' });
}
可以从"foo"
这里删除Object.keys(this.state)
吗?
该replaceState方法看起来像明显的方法来尝试,但它是不被提倡。
你可以设置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>
);
}
});
更新
以前的解决方案只是从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>
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>
);
}
});
在ReactCompositeComponent.js
GitHub 上的 React 源码中有一个方法叫做_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 不会立即生效,而是批量更改,因此如果您尝试克隆整个 state 对象并仅更改一个属性,则可能会清除之前对 setState 的调用。正如 React 文档所说;
React 可以将多个 setState() 调用批处理为单个更新以提高性能。
因为 this.props 和 this.state 可能会异步更新,所以你不应该依赖它们的值来计算下一个状态。
您实际上可以做的是添加更多信息;
this.setState({ xSet: true, x: 'foo' });
this.setState({ xSet: false, x: undefined });
这是丑陋的,当然,但它为您提供了额外的信息,您需要区分设置为未定义的值和根本未设置的值。此外,它与 React 的内部结构、事务、状态更改批处理和任何其他恐怖功能配合得很好。最好在这里采取一些额外的复杂性,而不是尝试猜测 Reacts 的内部结构,它充满了诸如事务协调、管理诸如 replaceState 之类的已弃用功能等恐怖事件
以前的解决方案 - 是反模式,因为它改变了 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
, 删除存储中不必要的键,但现在已弃用
您可以使用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}
>×</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>