282017.10

slickgrid中getItem, getItemById, getItemByIdx和row, id, index

在原始数据中,使用index的概念:
1> 使用getItemByIdx(index)来获取某个数据
2> 通过getIdexById(id)来获取它在原始数据中的index

在当前的视图中(有的情况下和原始数据不同,比如排序、筛选、tree收缩状态下),使用row的概念:
1> 使用getItem(row)来获取某个数据
2> 通过getRowById(id)来获取它在当前视图中的row

使用id就可以干任何事情:
1> 使用getItemById(id)来获取某个数据
2> 通过getRowById(id)来获取它在当前视图中的row
3> 通过getIdexById(id)来获取它在原始数据中的index

比如想通过当前视图中某行数据的row来获取它在原始数据中的index,就必须通过getItem(row)->item.id->getIdxById(id)这样的方法来获取。

21:01:00 已有1条回复
  1. 感谢
    #837 会飞的小蜗牛 2019-08-25 20:53 回复
272017.10

flex盒子模型的grow部分子元素宽高不能用百分比

在使用flex盒子模型一段时间之后,发现这货的坑真是不少,其中最最让人难理解的,就是它的flex-grow部分的子元素无法使用100%的对应尺寸。下面来举个例子:

<div style="display: flex; width: 300px; height: 200px;">
  <div style="width: 100px;"></div>
  <div style="flex-grow: 1">
  <div style="width: 100%; height: 100%; overflow: auto;"></div>
  </div>
</div>

上面的代码,我们希望创建一个盒子,盒子左边有一个固定100px宽度的div,右边说一个填满剩余空间的弹性伸缩div。而在这个弹性伸缩的div内部,我们希望放一个塞满整个区域的容器,容器设置了overflow: auto,因此,当这个容器内的内容超出可视区域时,会出现滚动条。

然而,事情没有想像的那么容易,上面的width: 100%并不会按我们想象的方式,使用它的父元素的宽度,而是会使用300px。

这是因为,再css标准里面,width/height如果是百分比的话,必须为它的parent提供一个确定的width/height,当然,parent的width/height也可以从再上一层继承。而如果上面这个条件不成立的话,parent会继续向上冒泡,直到找到一个确定的对应宽高为止。

不幸的是,flex的flex-grow是不确定的,因此,这里的width: 100%不能按我们想象的方式展现。

如何解决这个问题呢?那就是再在flex-grow元素的内部使用flex布局,它自己是弹性伸缩的,而你可以使它的内部元素也是弹性伸缩的:

<div style="display: flex; width: 300px; height: 200px;">
  <div style="width: 100px;"></div>
  <div style="flex-grow: 1; display: flex;">
  <div style="flex-grow: 1; height: 100%; overflow: auto;"></div>
  </div>
</div>

这样修改之后,就可以达到我们的目的。

通过这个问题的分析,基本就掌握了flex里面的精髓。“弹性”代表着没有固定宽度,而想要占满弹性容器,就必须在该容器本身实现一个新的弹性盒子模型。

19:10:13 已有0条回复
112017.10

react异步处理多次渲染的解决方案

react的组件有一个非常烦的操作,就是异步数据渲染。页面第一次打开的时候,会去mount所有的组件,但是,这个时候往往都是没有数据的,特别是通过数据驱动的SPA。大部分的方案都是在componentDidMount中请求数据,得到数据后调用this.setState更新数据。这个方案对于单个组件是没有问题的,但是,当数据来源不同,有一部分来自父组件,有一部分来自自身,就比较麻烦。有这么一个场景,父组件请求完数据之后,调用自己的setState,导致子组件界面重新渲染了,子组件此时请求数据后也setState,导致自己的界面重新渲染,这时没有父组件任何事。但是麻烦的事情接着来了,子组件自己请求数据重新渲染之后,父组件再次请求数据,虽然父组件并没有修改子组件的props,但还是导致子组件重新渲染,这时,子组件上一次请求的数据导致的渲染结果会被清空,整个子组件全部重绘了。实际上,我们希望保持子组件自己更新后的状态,并和父组件传来的props有一个merge的过程。

这种情况的大部分解决方案,是禁止子组件自己请求数据,请求数据由父组件全权代理,通过父组件的setState更新子组件的props,在子组件的componentWillReceiveProps中去更新子组件的state来达到更新子组件的视图的目的。

但是,如果我不打算采用这个方案,那么该怎么办?

还是有办法的,关键在于子组件的componentWillReceiveProps和shouldComponentUpdate。

第一个想法是只通过shouldComponentUpdate进行控制:

shouldComponentUpdate(nextProps, nextState) {
  return !isEqual(nextProps, this.props) || !isEqual(nextState, this.state);
}

isEqual是lodash的方法。这样可以判断,当新的state和新的props有任何一个有变动时,才会更新界面。然而,这需要开发者非常注意:

1.绝对不能直接操作state,举个例子:

let { items } = this.state
items.push(1)
this.setState({ items })

这个操作非常危险,看上去setState的操作是合法的,虽然你修改了state,但是你用新的是值去setState,然而,如果你这样操作,shouldComponentUpdate中的nextState将会是被push了新值的state,判断会错误。正确的做法是:

let { items } = this.state
items = items.concat(1)
this.setState({ items })

一定要用一个copy,而非直接修改state。

2.不能在componentWillReceiveProps中setState,在我的前面一篇文章里面说过,componentWillReceiveProps内部的setState是同步的,会马上更新state而不触发重绘,所以,如果你在componentWillReceiveProps中setState,那么shouldComponentUpdate中的nextState会是经过componentWillReceiveProps修改过的。

第1个问题是可以避免的,在万不得已的情况下,我们甚至可以动用lodash.cloneDeep,但是第2个问题,我们需要谨慎处理,因为我们不可能不在componentWillReceiveProps中调用setState,这时不可能的,我们异步数据处理方案只能这样去做。那么怎么解决这个问题呢?

那就是,在componentWillReceiveProps中做进一步判断,是否要更新setState,大部分情况下,如果父组件虽然重绘,但是传给子组件的props不变的话,这个子组件不需要重绘,所以,在componentWillReceiveProps中也加入一个判断:

componentWillReceiveProps(nextProps) {
  if (isEqual(this.props, nextProps)) {
    return
  }

  // ...

  this.setState({ ... })
}

shouldComponentUpdate(nextProps, nextState) {
  return !isEqual(nextProps, this.props) || !isEqual(nextState, this.state);
}

这样的处理就极为巧妙了。当父组件传给子组件的props发生变化时,子组件整个重置,用新的props重绘(之前的setState造成的改动被重置),而如果父组件传的props不变时,就不进行任何动作。

这个问题,带来了react组件的另外一个思考,如果我们的大部分组件都是依赖异步数据的,那么组件的生命周期是不是存在问题?componentDidMount的意义何在呢?难道仅仅是绑定一些事件之类的?

17:50:30 已有3条回复
  1. 哈喽,看到你这篇文章写得蛮清楚的,我遇到了一个类似的问题,想请教一下~
    当我在来回快速切换菜单的时候,每个菜单didmount的时候都分别请求各自的数据,我在上一个请求没有完成时,就手动点了下一个菜单,这时先先展现了菜单B,而后菜单A的响应完成后,又重新渲染了菜单A的view,导致我真正想要看的菜单B的view没有被渲染,请问你对这个问题有了解嘛~
    (PS: 446875622 这是我的微信号,方便的话可以加我一下,我仔细请教,麻烦了~)
    #673 一个装睡的人 2018-12-11 20:16 回复
  2. deferer-queue了解一下 https://www.tangshuang.net/6092.html
    #674 回复给#673 否子戈 2018-12-11 20:28 回复
  3. 好的,蟹蟹~
    #675 回复给#674 一个装睡的人 2018-12-11 20:30 回复