如何从 React 组件的状态对象中删除属性
How can I remove an attribute from a React component's state object
如果我有一个在其状态上设置了 属性 的 React 组件:
onClick() {
this.setState({ foo: 'bar' });
}
是否可以从 Object.keys(this.state)
中删除此处的 "foo"
?
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
和一个 parent 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>
之前的解决方案 - 是反模式,因为它改变了 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
,删除存储中不必要的键,但现在是 deprecated
您可以使用 Object.assign
to make a shallow copy of your application's state at the correct depth and delete the element from your copy. Then use 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>
在 GitHub 上的 React 源码 ReactCompositeComponent.js
中有一个名为 _processPendingState
的方法,它是实现从对 component.setState 的调用合并状态的最终方法;
```
_processPendingState:功能(道具,上下文){
var inst = this._instance;
var queue = this._pendingStateQueue;
var replace = this._pendingReplaceState;
this._pendingReplaceState = 假;
this._pendingStateQueue = 空;
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 may batch multiple setState() calls into a single update for performance.
Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.
您实际上可以做的是添加更多信息;
this.setState({ xSet: true, x: 'foo' });
this.setState({ xSet: false, x: undefined });
当然,这很丑陋,但它为您提供了区分设置为未定义的值和根本未设置的值所需的额外信息。此外,它与 React 的内部结构、事务、状态更改批处理和任何其他可怕的东西配合得很好。最好在这里采取一些额外的复杂性,而不是尝试事后猜测 Reacts 的内部结构,它充满了可怕的事物,例如事务协调、管理 replaceState 等已弃用的功能等
如果删除是在一个函数中并且键需要是一个变量,试试这个:
removekey = (keyname) => {
let newState = this.state;
delete newState[keyname];
this.setState(newState)
// do not wrap the newState in additional curly braces
}
this.removekey('thekey');
与史蒂夫的回答几乎相同,但在函数中。
如果你想完全重置状态(删除大量项目),像这样的方法可行:
this.setState(prevState => {
let newState = {};
Object.keys(prevState).forEach(k => {
newState[k] = undefined;
});
return newState;
});
使用 setState
的这种变体允许您在调用期间访问整个状态,而 this.state
可能有点过时(由于之前的 setState
调用尚未已完全处理)。
我认为这是解决问题的好方法 =>
//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 });
}
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>
);
}
});
唯一的方法是创建深层副本,然后从深层克隆中删除 属性 并从 setState 更新程序函数中删除 return 深层克隆
this.setState(prevState => {
const newState = {
formData: {
...prevState.formData,
document_details: {
...prevState.formData.document_details,
applicant: {
...prevState.formData?.document_details?.applicant
keyToBeDeleted: dummVAlue //this is redundant
}
}
}
};
delete newState.formData.document_details.applicant.keyToBeDeleted;
return newState;
});
import dotPropImmutable from "dot-prop-immutable";
onClick() {
this.setState(
dotPropImmutable.delete(this.state, 'foo')
);
}
当我们使用undefined
或null
来删除一个属性时,我们实际上并没有删除它。因此,对于 Javascript 对象,我们应该在 属性:
之前使用 delete
关键字
//The original object:
const query = { firstName:"Sarah", gender: "female" };
//Print the object:
console.log(query);
//remove the property from the object:
delete query.gender;
//Check to see the property is deleted from the object:
console.log(query);
然而,在 React Hooks 中我们使用钩子,上述方法可能会导致一些错误,特别是当我们使用效果来检查状态变化时的某些东西时。为此,我们需要在移除一个 属性:
后设置状态
import { useState, useEffect } from "react";
const [query, setQuery] = useState({firstName:"Sarah", gender:"female"});
//In case we do something based on the changes
useEffect(() => {
console.log(query);
}, [query]);
//Delete the property:
delete query.gender;
//After deleting the property we need to set is to the state:
setQuery({ ...query });
如果我有一个在其状态上设置了 属性 的 React 组件:
onClick() {
this.setState({ foo: 'bar' });
}
是否可以从 Object.keys(this.state)
中删除此处的 "foo"
?
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
和一个 parent 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>
之前的解决方案 - 是反模式,因为它改变了 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
,删除存储中不必要的键,但现在是 deprecated
您可以使用 Object.assign
to make a shallow copy of your application's state at the correct depth and delete the element from your copy. Then use 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>
在 GitHub 上的 React 源码 ReactCompositeComponent.js
中有一个名为 _processPendingState
的方法,它是实现从对 component.setState 的调用合并状态的最终方法;
``` _processPendingState:功能(道具,上下文){ var inst = this._instance; var queue = this._pendingStateQueue; var replace = this._pendingReplaceState; this._pendingReplaceState = 假; this._pendingStateQueue = 空;
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 may batch multiple setState() calls into a single update for performance.
Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.
您实际上可以做的是添加更多信息;
this.setState({ xSet: true, x: 'foo' });
this.setState({ xSet: false, x: undefined });
当然,这很丑陋,但它为您提供了区分设置为未定义的值和根本未设置的值所需的额外信息。此外,它与 React 的内部结构、事务、状态更改批处理和任何其他可怕的东西配合得很好。最好在这里采取一些额外的复杂性,而不是尝试事后猜测 Reacts 的内部结构,它充满了可怕的事物,例如事务协调、管理 replaceState 等已弃用的功能等
如果删除是在一个函数中并且键需要是一个变量,试试这个:
removekey = (keyname) => {
let newState = this.state;
delete newState[keyname];
this.setState(newState)
// do not wrap the newState in additional curly braces
}
this.removekey('thekey');
与史蒂夫的回答几乎相同,但在函数中。
如果你想完全重置状态(删除大量项目),像这样的方法可行:
this.setState(prevState => {
let newState = {};
Object.keys(prevState).forEach(k => {
newState[k] = undefined;
});
return newState;
});
使用 setState
的这种变体允许您在调用期间访问整个状态,而 this.state
可能有点过时(由于之前的 setState
调用尚未已完全处理)。
我认为这是解决问题的好方法 =>
//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 });
}
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>
);
}
});
唯一的方法是创建深层副本,然后从深层克隆中删除 属性 并从 setState 更新程序函数中删除 return 深层克隆
this.setState(prevState => {
const newState = {
formData: {
...prevState.formData,
document_details: {
...prevState.formData.document_details,
applicant: {
...prevState.formData?.document_details?.applicant
keyToBeDeleted: dummVAlue //this is redundant
}
}
}
};
delete newState.formData.document_details.applicant.keyToBeDeleted;
return newState;
});
import dotPropImmutable from "dot-prop-immutable";
onClick() {
this.setState(
dotPropImmutable.delete(this.state, 'foo')
);
}
当我们使用undefined
或null
来删除一个属性时,我们实际上并没有删除它。因此,对于 Javascript 对象,我们应该在 属性:
delete
关键字
//The original object:
const query = { firstName:"Sarah", gender: "female" };
//Print the object:
console.log(query);
//remove the property from the object:
delete query.gender;
//Check to see the property is deleted from the object:
console.log(query);
然而,在 React Hooks 中我们使用钩子,上述方法可能会导致一些错误,特别是当我们使用效果来检查状态变化时的某些东西时。为此,我们需要在移除一个 属性:
后设置状态import { useState, useEffect } from "react";
const [query, setQuery] = useState({firstName:"Sarah", gender:"female"});
//In case we do something based on the changes
useEffect(() => {
console.log(query);
}, [query]);
//Delete the property:
delete query.gender;
//After deleting the property we need to set is to the state:
setQuery({ ...query });