在哪里放置修改状态的`componentWillMount`初始化函数?

时间:2019-03-24 20:02:10

标签: javascript reactjs

由于将不推荐使用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需要初始化才能安装组件。 在构造函数中不能调用此函数,因为它使用了setStateinitGrid函数还用于代码库的其他部分,因此它仍然需要setState才能在其中运行。

我缺少明显的东西吗?解决此问题的专业方法是什么?

3 个答案:

答案 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.gridthis.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

    这对我来说是最专业的解决方案