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 回复
302017.9

why react component bind this in constructor

In many react component's constructor, developers always bind its methods with this, like:

export default class MyComponent extends Component {
  constructor() {
    this.say = this.say.bind(this)
  }
  ...
}

This sentence seems to be stupid. Is this regulated by react?

No!

In fact, react component class follow ES6 class std. The reason to put this sentence is because when you use this.say in your render, you alway use it as onClick callback function, this is why this is not point to instance of this component. Let's look into this code:

<a href="" onclick="alert(this.href)">click</a>

You know what this point to here, so when you use a component method as a event callback function, this in the function does not referer to the instance of component. So another way to solve this problem is to bind this when you use the method, like:

export default class MyComponent extends Component {
  constructor() {
    // do not use bind sentence
  }
  render() {
    return <a onClick={this.say.bind(this)}>click</a>
  }
}

Now you can use this in say as want you want.

01:34:09 已有0条回复
272017.9

网上看到这个前景色和背景色配色方案。放在这里备用。

前景与背景配色方案

非得写一行字,有意思么。。。

23:19:52 已有0条回复

当我从sublime转到atom时,以为遇到了真爱,但是卡卡的atom简直不忍直视,总感觉这货随时会挂掉,进王者峡谷智能是脆皮坦克一般的存在。当我抱着试一试VS CODE的心态,下载安装了之后,瞬间那种喜悦感,恨不得把自己电脑上最大的数据库文件打开来试试。VS CODE比atom更精美,速度更快,不带有疯狂的package社区,专注写代码。特别是底部打开时候的紫色工具条,太艳了。很快,我就从一个厌恶微软产品的人,转而喜爱上这款微软发布的开源编辑器。在我几乎所有的电脑上卸载掉以前的编辑器,专门用VS CODE编程。

不足之处就是快捷键和sublime不一样,要习惯很久,而且有些很实用的快捷键也不一样,导致经常按错。没办法,要舒服的躺进一个坑,就要痛苦的从前面的坑里爬出来。

21:00:06 已有0条回复
222017.9

run Photoshop CS2 on Windows10

When I downloaded the free old version of Adobe Photoshop CS2 and installed it on windows10, I found it is not able to launch the software, it comes out with a error alert, and force me to close the program. I was aware of it is a compatibility problem, but not know how to fix it. After tried a lot, I found the most easy way: using windows application tools.

  1. close your photoshop cs2 alert first
  2. open your photoshop cs2 installed directory, and select Photoshop.exe
  3. on the top of the screen, there will be a tab named Manage
  4. click Troubleshoot compatibility item
  5. follow what it call you to do, try more
  6. done

And you may need to run photoshop as administrator, for this, right click on Photoshop.exe and open properties, change to Compatibility to select Run this program as an administrator.

14:18:10 已有0条回复
122017.9

在最开始接触一个依赖的时候,把关于^~等符号的版本管理弄清楚的时候,觉得这个好伟大,然而当躺进坑里,才发现,这东西是害人的。对一个第三方package的依赖,默认安装会在版本前面加^,当第二次执行npm install的时候,如果发现npm服务器上存在比该版本大的小版本号,就会使用新的版本的package。然而,然而!当新的版本对当前环境不兼容时,特别是那些被依赖的很深的package发生这种情况,对于开发者而言,简直就是噩梦。昨天做build的时候一切还正常,连代码都没改动,今天再做build的时候,却怎么也通不过了。造成这个问题的原因,是因为所有的package的版本都向后兼容,虽然你的项目代码没有变,但是你的第三方依赖代码变了,如果新的第三方代码不兼容你当前的node版本,那么就是一个惨字。解决的唯一办法,是通过升级npm版本,升级之后,package都是扁平化的,然后再在package.json文件中,把那个不兼容的package固定在较低的版本中。

我个人的观点是,一个项目,之所以能够正常运行,是因为在开发时选择了适合的版本,而且是经过测试的。倘若在第二次运行时,代码不同,那么怎么保证之前的测试是有效的呢?因此,npm install不应该自作主张的从服务器选择更新的版本,而是在本地安装允许的最新版本。比如jquery,安装本地所有package中允许的最新版本,而不是比本地允许的版本还要新的版本。

09:21:05 已有0条回复
062017.9

在一个软件中,使用多个语言进行混合编程,不同的服务用最好的语言去执行得到结果,例如node和Python混合,php和c++。那么怎么实现呢?其实语言之间的混合编程原理很简单,以一个语言为入口程序,另一个语言为后台程序,在入口程序中如何调用后台程序的执行结果呢?那就要用到入口程序的execute能力,通过调用命令行工具,执行后台程序,并且有非常便捷的方式得到后台程序的执行结果。

这里有一篇如何实现node和python混合的文章,简单的说,就是利用node child_process能力,通过exec执行一个命令行命令,这个命令行命令调用python程序执行,并且把执行结果输出在child_process的stdout中,node在对stdout的输出结果进行解析即可。

说到这里,你就发现多语言混合编程也并不困难了。

11:14:07 已有0条回复

量化分析数据资源

https://github.com/thuquant/awesome-quant

发现一个超级棒的量化分析资源收集页面

302017.8

HHuploadify 升级,一个风骚的版本

HHuploadify是一个纯粹的图片上传组件,说纯粹是因为只考虑把它用来上传图片。当然,上传文件也是没问题的,但是文件上传的UI应该和图片上传的UI有较大的区别,而且文件上传也不存在preview的问题。

为什么要升级?

HHuploadify最初的版本是我在Huploadify这个jquery插件的基础上开发的,UI改了,上传的时候支持的功能多了。在发布后的几年里,不断有人问我xxx可不可以,我的大部分回答,都是要自己写代码实现。这几天因为公司产品release之后相对有点闲了,就打算重写一遍HHuploadify。

大概花了两天的时间,重构过程还算顺利,最开始想支持IE8,但最后因为iframe提交时报dined错,就只能放弃了。现在插件能支持到IE9(IE9也有一点坑,但是大部分情况下还过得去)。而且,最重要一点,它不依赖jquery了。它是一个独立的组件,而且我发布到npm了。我这两年都在用webpack babel等构建工具,所以依赖感比较强,写ES6也很随意,所以源码都是ES6写的,ES6归ES6,但是有些api要考虑IE9,所以还是没敢太大胆用,比如还是在用for...in。但是,我已经习惯了直接import, export这样的语法,所以,当我想重新用HHuploadify的时候,发现不好用,它只是一个jquery插件。这就催生我重写一遍它。对于那些还在用或者准备要用的同学们,我可能要很遗憾的说,你想要实现的都可以实现,但是你要写的代码可能更多了。之所以这样,是因为我之前封装了几个函数,现在把它们去掉了,整个组件的核心都在这里,只需要通过class extends或者钩子函数就可以非常好的进行扩展。

看看DEMO

所以,它现在变得很风骚,不信你可以先看看效果:

最初级的配置,选择图片(多选)后点击上传按钮进行上传

添加一个auto的配置,选择图片(多选)之后自动上传图片

默认情况下,点击选择图片按钮后,打开的选择器可以一次性选择多张图片。如果是用户头像,那么只能选一张图片,只需要配置一个single选项即可。配置之后,这个实例只能上传一张图片。开始上传之后,选择按钮就会消失。

上传成功之后,用server端返回的url字段中的url作为新的预览图片。在一些需要裁剪的情况下可能会用到,只需要把showPreview设置为2即可。

通过showUploadProcess的配置,采用不同的上传进度展示效果。

假如你想默认就有几张图片,也是可以的,使用reset方法即可。

对HHuplodify进行扩展也超级简单,上图演示的就是利用jquery.dragsort插件扩展HHuploadify,对上传之后的图片列表可以拖拽的效果。

利用single选项,进行简单扩展之后,就可以实现一组固定的上传。

通过钩子函数,对上传的最大张数进行控制。上面图片演示中,最多只能上传4张图片。

这些场景应该很多都会覆盖你的上传的情况吧。当然,你有特殊的需求,没关系,只需要extends class或者在钩子函数中写逻辑即可。可配性强的同事,接口还是完全暴露可复写的。

安装和使用

你可以直接像以前一样在浏览器中直接引用,但是你也可以像module一样import到你的项目中,我已经发布了npm包,所以你可以这样干:

npm i -S hhuploadify
import 'hhuploadify/dist/HHuploadify.css' // 在webpack中可以直接使用css了import HHuploadify from 'hhuploadify'

let uploader = new HHuploadify({

  container: '#upload',

  url: 'http://localhost/upload',})

然后,在那个#upload的容器中就会有一个上传的UI效果出来。uploader就是这个实例了,它包含了所有的methods,你可以用它干你想干的事。当然,你也可以在配置中传入钩子函数来干你想干的事。

看下怎么扩展它:

import HHuploadify from 'hhuploadify'

export default class extends HHuploadify {

  myMethod() { // 新方法

  

  console.log(this.files)

  }

  reset(files) { // 复写的方法

  

  super.reset(files)

  

  console.log(files)

  }}

这样你就扩展出一个新方法,复写了一个原型链方法。是不是非常简单。

下载和仓库

如果你看了网上的一些资料,按照它们提供的方法来使用,可以下载1.0版本,如果你想给我提bug或者contribute的话,在github仓库上玩弄吧。

18:02:24 已有1条回复
  1. 这个好可以
    #867 这个好可以 2019-12-09 23:17 回复
212017.8

机器学习入门资源不完全汇总