React Main Concepts - 5. State and Lifecycle 列出规则
关注:
正确使用状态
关于setState(),你应该知道三件事。
不要直接修改状态
例如,这不会重新渲染组件:
// Wrong
this.state.comment = 'Hello';
改为使用setState():
// Correct
this.setState({comment: 'Hello'});
您可以分配this.state 的唯一位置是构造函数。
状态更新可能是异步的
React 可以将多个 setState() 调用批处理到一个更新中
性能。
因为this.props和this.state可能会异步更新,
你不应该依赖它们的值来计算下一个状态。
例如,这段代码可能无法更新计数器:
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
要修复它,请使用接受函数的setState() 的第二种形式
而不是一个对象。该函数将接收先前的状态
作为第一个参数,更新时的道具是
作为第二个参数应用:
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
我们在上面使用了arrow function,但它也适用于常规
功能:
// Correct
this.setState(function(state, props) {
return {
counter: state.counter + props.increment
};
});
合并状态更新
当你调用setState() 时,React 会将你提供的对象合并到
当前状态。
例如,您的状态可能包含几个独立变量:
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
然后您可以使用单独的setState() 独立更新它们
调用:
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
合并很浅,所以this.setState({comments})离开
this.state.posts 完好无损,但完全替换
this.state.comments.
使用您提供的代码 sn-p 您打破了其中两个规则。让我们
首先处理导致 JSLint 警告的那个:
防止在 this.setState 中使用 this.state
(react/no-access-state-in-setstate)
您收到此警告的原因是:
状态更新可能是异步的......因为this.props 和
this.state 可能会异步更新,不要依赖
它们的值用于计算下一个状态。
newRows 和 tempSort 都基于之前的状态。这
JSLint 警告说你应该使用的回调变体
setState().
onSortChange = i => {
this.setState(({ rows: tempRows, sort: tempSort }) => {
// sort
return { rows: newRows, sort: tempSort };
});
};
处理完警告后,让我们看看另一个问题
sn-p。 sort() 对元素进行就地排序,这意味着
原作修改。同样tempSort.col = [i] 和tempSort.asc = !tempSort.asc 也都修改当前状态。 分配一个
新变量的对象不会创建副本。这两个变量都会
简单地引用同一个对象。
const object_1 = { a: 1 };
const object_2 = object_1;
object_2.a = 2;
console.log("object_1", object_1);
console.log("object_2", object_2);
确保在改变对象或使用之前复制对象
就地方法。
const tempRows = [...this.state.rows];
const tempSort = {...this.state.sort};
在将数组/对象分配给之前,两者都创建了一个浅表副本
一个变量。
如上所述,解决方案可能如下所示:
/* Comparator factories. sort[true] is ascending, sort[false] is descending.
* This factory accepts a function that is called for both a and b,
* comparing their return values.
*
* items.sort(sort[true](item => item.a))
*
* Sorts an array of items based on their "a" property in ascending order.
*/
const sort = {
true: (fn) => (a, b) => (a = fn(a), b = fn(b), -(a < b) || +(a > b)),
// flip a, b arguments for descending order
false: (fn) => (a, b) => sort[true](fn)(b, a),
};
class GenericTable extends React.Component {
constructor(props) {
super(props);
this.state = { ordered: props.data };
}
handler(i) {
// use a callback because the new state depends on the old state
this.setState(({ordered, i: iPrevious, asc}) => {
asc = i != iPrevious || !asc;
return {
i, asc,
// copy `ordered` before using the in-place `sort` method
ordered: [...ordered].sort(sort[asc](row => row[i]))
};
});
}
render() {
return (
<table>
<thead>
<tr>{this.props.headers.map((header, i) => (
<th key={i} onClick={() => this.handler(i)}>{header}</th>
))}</tr>
</thead>
<tbody>
{this.state.ordered.map((row, i) => (
<tr key={i}>
{row.map((cell, i) => <td key={i}>{cell}</td>)}
</tr>
))}
</tbody>
</table>
);
}
}
ReactDOM.render(
<GenericTable
headers={["a", "b", "c"]}
data={[[1, 5, 10], [6, 11, 2], [12, 3, 7]]}
/>,
document.querySelector("#root")
);
th{cursor: pointer}th,td{border:1px solid black}
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<div id="root"></div>