由于将不推荐使用componentWillMount
,所以我想知道推荐的处理初始化状态的初始化函数的方法。我知道关于componentWillMount
的一般方法是将事物移入构造函数或componentDidMount
中,但是在这种情况下两者都存在问题。
连接四个示例代码(忽略了不必要的代码):
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
rows: 6, // # rows of the grid; cols = rows + 1
grid: [],
fullColumns: [],
// ...
};
}
componentWillMount() {
this.initGrid(this.state.rows);
}
initGrid(rows) {
// Create grid data structure to keep track of which grid cells
// contain checkers of which color:
// 'grid' = array of COLUMN arrays!
let grid = [];
// Create the 'fullColumns' array to keep track of which grid columns are full:
let fullColumns = [];
let cols = rows + 1;
for (let c = 0; c < cols; c++) {
fullColumns.push(false);
let column = [];
for (let r = 0; r < rows; r++) {
column.push(null);
}
grid.push(column);
}
this.setState({
rows,
grid,
fullColumns,
});
}
// ...
render() {
// ...
}
}
我无法将this.initGrid
函数放在componentDidMount
中,因为this.state.grid
需要初始化才能安装组件。
在构造函数中不能调用此函数,因为它使用了setState
。 initGrid
函数还用于代码库的其他部分,因此它仍然需要setState
才能在其中运行。
我缺少明显的东西吗?解决此问题的专业方法是什么?
答案 0 :(得分:2)
您可以在构造函数中使用它,只需替换
this.setState({
rows,
grid,
fullColumns
});
使用
this.state = {
...this.state
rows,
grid,
fullColumns
}
您还可以通过在组件状态未完全初始化时呈现initGrid
或提供有意义的默认值来将componentDidMount
移至null
方法。
render() {
if(this.state.grid) {
return ...
}
else {
return null;
}
}
答案 1 :(得分:0)
initGrid
完全不需要成为组件的一部分,它不绑定任何状态,只是尝试更新它。
它可以是一个辅助功能,可以在需要的地方使用:
function initGrid(rows) {
...
return {
rows,
grid,
fullColumns,
};
}
class App extends React.Component {
state = initGrid(6);
...
}
答案 2 :(得分:0)
我一直在考虑这个问题一段时间,并提出了几种解决方案,所有这些都有自己的权衡。我将在此处进行记录,以便它们对于遇到此问题的其他人仍然有用。选择最符合您项目需求的解决方案:
创建一个重复的initGrid
函数,仅在构造函数中使用(直接分配给this.state
而不是使用setState
)。除了重复代码和明显违反DRY之外,这还创建了一个额外的'单一用途'方法,该方法会导致“请勿直接更改状态,请使用setState()”警告,烦人。
使用匿名自调用函数,该函数直接在构造函数中分配给this.state.grid
和this.state.fullColumns
:
constructor(props) {
// ...
[this.state.grid, this.state.fullColumns] = (function(rows) {
let grid = [];
let fullColumns = [];
let cols = rows + 1;
for (let c = 0; c < cols; c++) {
fullColumns.push(false);
let column = [];
for (let r = 0; r < rows; r++) {
column.push(null);
}
grid.push(column);
}
return [grid, fullColumns];
})(this.state.rows);
}
componentWillMount
现在可以删除。这也摆脱了“请勿直接更改状态,请使用setState()”警告,并且不会毫无意义地创建另一种方法供单次使用。 但是它仍然不必要地复制了this.initGrid
中的代码。
只需对状态下的初始grid和fullColumns数组进行硬编码:
constructor(props) {
super(props);
this.state = {
rows: 6,
grid: [
[null, null, null, null, null, null],
[null, null, null, null, null, null],
[null, null, null, null, null, null],
[null, null, null, null, null, null],
[null, null, null, null, null, null],
[null, null, null, null, null, null],
[null, null, null, null, null, null],
],
fullColumns: [false, false, false, false, false, false, false]
}
}
componentWillMount
现在可以删除。这确实牺牲了可定制性,并且感觉很不专业。然而,它并非没有自己的优势。这样可以减少逻辑,提高效率,还有助于可视化/记录代码。
修改initGrid
函数以区分从构造函数调用还是从其他地方调用:
initGrid(rows) {
// Create grid data structure to keep track of which grid cells
// contain checkers of which color:
// 'grid' = array of COLUMN arrays!
let cols = rows + 1;
let grid = [];
let fullColumns = [];
for (let c = 0; c < cols; c++) {
fullColumns.push(false);
let column = [];
for (let r = 0; r < rows; r++) {
column.push(null);
}
grid.push(column);
}
// If 'this.state.grid' is already initialised (i.e. non-empty),
// the call is NOT from inside the constructor:
if (this.state.grid.length !== 0) {
this.setState({
rows,
grid,
fullColumns,
});
} else {
// If the grid is empty, this call came from inside the constructor,
// so we disable the "Do not mutate state directly, Use setState()" warning:
// eslint-disable-next-line react/no-direct-mutation-state
this.state = {
...this.state,
grid,
fullColumns,
}
}
}
这不会不必要地重复代码,并且仍然保持可定制性。它仍然具有“请勿直接更改状态,请使用setState()”警告,但它会用// eslint-...
注释使之静音。现在我们可以在构造函数中而不是在this.initGrid(this.state.rows)
中调用componentWillMount
。
这对我来说是最专业的解决方案。