React.js子菜单维度仅在ComponentDidUpdate之后可用

时间:2015-10-31 17:50:39

标签: reactjs

请考虑以下事项:

带有下拉子菜单系统的简单顶级菜单:

<div id="menuBar">
   <div id="menuItemA">
      <div ref="refMenuItemA-DropList"
           onMouseOut={ this.hideSubMenu.bind(this, this.props.item) }
           onMouseOver={ this.showSubMenu.bind(this, this.props.item) } 
             style={ { 
                     display: this.state.display, 
                     opacity: this.state.opacity, 
                     left: this.state.left, 
                     top: this.state.top, 
                     width: this.state.width 
                     } }> 
         <div id="subMenuItemA" ref="refSubMenuItem"> SubMenuItem A 
                <div id="subSubMenuItemA">subSubMenuItemA</div>
                      .......
                      .......
         </div>
         <div id="subMenuItemB"> SubMenuItem B </div>
         <div id="subMenuItemC"> SubMenuItem C </div>
      </div>
   <div>
</div>

在初始渲染时:

this.state = {
  opacity: 0,
  display: 'none',
  bottom: 0,
  height: 0,
  left: 0,
  right: 0,
  top: 0,
  width: 0,
  selected: ''
};

onMouseOver事件触发时,我可以轻松确定this.state.top & this.state.leftrefMenuItemA-DropList的影响,因为其父MenuItemA始终可见......

  1. 我可以检索refMenuItemA-DropList的参考值,找到它的父母的左边&amp;底部属性并将它们传递给状态;和,

  2. 更改&lt; DIV&GT;拥有refMenuItemA-DropList可见,并且在上面#1中给出了正确定位的左上角。

  3. 但我无法确定相同div的宽度,因为它的状态尚未变为可见。

  4. 此外,尝试检索&lt; div&gt;的顶部拥有refSubMenuItem,以及#3中提到的宽度值,并将它们传递给它的子项在同一mouse-over事件中将是徒劳的,因为状态值在componentDidUpdate之前不会更新,因此不透明度是仍为0且显示仍为“无”。

    问题:

    如果状态在componentDidUpdate之前没有更新,并且我无法在componentDidUpdate中设置状态,我怎样才能将值传递给子菜单项的子节点,以便一旦状态更新就可以正确对齐使元素可见,因为需要一个可见元素来检索大小/位置值?

1 个答案:

答案 0 :(得分:1)

我真的很想过这个。虽然ReactJS的文档强烈建议不要在componentDidUpdate中搞乱状态,但我研究得越多,就越多人指出没有别的办法了。

我没有收到任何错误,没有警告,应用程序没有缓慢,我有超过100条路线,就像一个旧的骡子我一次又一次地击败应用程序以找到任何退化,但没有。

为了不将您与上述内容混淆,在我的应用中,实际的引用名称不是 refMenuItemA-DropList1 but rather 1refFlyList

所以这是我的解决方案......

予。顶NAV-菜单item.js

  componentDidUpdate() {
    const self = this;
    if (self.refs.refFlyList !== undefined) {
      const refFlyList = self.refs.refFlyList.clientWidth;

      // need this or endless loop
      if (refFlyList === this.state.width) return;

      if (refFlyList !== 0) {
        this.setState( { width: refFlyList } );
      }
    }   
  }

 fetchSubMenu(node) {
    if (node.childNodes !== undefined) {
      const parentDims= {
        width: this.state.width,
        top: this.state.top,
        left: this.state.left
      };
      return (
        node.childNodes.map((childNode, index) => {
          return (
            <SubMenuItem key={ index }
                         node={ childNode }
                         parentDims={ parentDims }
            />
          );
        })
      );
    }
  }

II。子菜单item.js

  showSubMenu(node, event) {
    if (node.childNodes !== undefined) {
      const parentDims = this.props.parentDims;
      if (this.state.top === 0) {
        this.setState({
          left: parentDims.width,
          top: event.clientY - parentDims.top
        });
      }
      this.setState({
        opacity: 1,
        display: 'block',
        selected: ' selected'
      });
    }
  }

III。如果像我一样你使用的是linting helper(我使用EsLint):

  "rules": {
    "react/no-did-update-set-state": 0,
     .....................

我的react-router是动态的[数据来自Mongo],它是递归的。虽然我只能达到3个等级,但是我玩了6个等级,一切都运行良好。

解决方案并不像黑客那样,性能也很棒!