如果我有一个React组件,在其状态中设置了一个属性:
onClick() {
this.setState({ foo: 'bar' });
}
在这里从Object.keys(this.state)
中删除"foo"
是否可能?
replaceState方法看起来是一个可尝试的明显方法,但它已经被弃用了。
如果我有一个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
中删除键,则可能的一种解决方案是使用带有一个父级 key
的 setState
,如下所示:
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>
);
}
});_processPendingState
的方法,它位于ReactCompositeComponent.js
文件中。此方法是实现从组件setState
调用中合并状态的最终方法;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总是合并状态,因此已删除的属性将被忽略。你可以考虑添加更多信息;出于性能考虑,React可能会将多个setState()调用合并为一次更新。
由于this.props和this.state可能会异步更新,因此您不应该依赖它们的值来计算下一个状态。
this.setState({ xSet: true, x: 'foo' });
this.setState({ xSet: false, x: undefined });
这看起来有点丑,但它为你提供了额外的信息,帮助你区分一个被设置为undefined的值和根本没有设置值的情况。而且它与React的内部、事务、状态改变批处理以及其他任何恐怖的东西都非常兼容。在这里多花一点复杂度比试图猜测React的内部要好,因为它们充满了像事务协调、管理已弃用的功能(如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
,它可以删除store中不必要的键,但现在它已经被弃用
Object.assign()
不会进行深度克隆,在更复杂的应用程序中可能会无意中直接修改状态。第二个问题存在于您的两个解决方案中,并且与@alexander-t的答案相同。您没有按照要求从Object.keys()
中删除键,而是只更改了其值。 - Vincenull
/undefined
将至少生成一个空的列表项或表格行。但是,如果代码编写成使用子对象的属性,则将该值设置为 null
/ undefined
将导致难以排除的错误。 - Vince在 JavaScript 对象中,当我们使用 undefined
或者 null
来移除一个属性时,实际上并没有真正删除它。因此,我们应该在属性之前使用 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中我们使用hooks,上述方法可能会导致一些bug,特别是当我们使用effects检查状态改变时。为此,我们需要在删除属性后设置状态:
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 });
Object.assign
深度拷贝你的应用状态,并删除你拷贝的元素。然后使用 setState
将修改后的拷贝合并回应用状态中。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>
唯一的方法是创建一个深度复制,然后从深拷贝中删除该属性,并从setState更新程序函数返回深度克隆。
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')
);
}
我认为这是一个不错的方法 =>
//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 });
}
removekey = (keyname) => {
let newState = this.state;
delete newState[keyname];
this.setState(newState)
// do not wrap the newState in additional curly braces
}
this.removekey('thekey');