参考的原文章:函数式组件与类组件有何不同?
开门见山:函数式组件获取了render时所需要的值。
那我们如何理解这句话?
举例说明
点击这个链接查看例子:通过类组件去关注时,然后在延时的过程中去切换用户,就会发现关注的目标用户错乱了
简化上面的链接的代码,有如下一个类组件:
class ProfilePage extends React.Component {
showMessage = () => {
alert('Followed ' + this.props.user);
};
handleClick = () => {
setTimeout(this.showMessage, 3000);
};
render() {
return <button onClick={this.handleClick}>Follow</button>;
}
}
以及这样一个函数式组件
function ProfilePage(props) {
const showMessage = () => {
alert('Followed ' + props.user);
};
const handleClick = () => {
setTimeout(showMessage, 3000);
};
return (
<button onClick={handleClick}>Follow</button>
);
}
这两种组件的方式,本意上都是点击一个按钮去关注某个用户,然后用setTimeOut去模拟网络请求提示结果到达异步的一个效果。
点击查看上面链接的用例,尝试按照以下顺序来分别使用这两个按钮:
- 点击 其中某一个 Follow 按钮。
- 在3秒内 切换 选中的账号。
- 查看 弹出的文本。
产生这样错误原因是因为什么呢?
来看看类组件中 showMessage
的方法
showMessage = () => {
alert('Followed ' + this.props.user);
};
这个类方法从 this.props.user 中读取数据。
在 React 中 Props 是不可变,props作为单向数据流,它只能由父组件流向子组件,这里不可变的意思就是,在一个确定的状态下,这个props是永远不变的。
然而 this 是可变的!实际上这就是类组件中 this 的意义。因为组件是可复用的,所以类组件中的 this 就是以便你能在渲染方法或者生命周期中得到不同的实例(也可以说是最新的实例)。
所以,当我们模拟异步请求的时候,我们又进行了切换操作,使得组件进行了重新渲染,this 就改变了,此时获取的 props 也是最新的。
从React的设计理念上来说,UI 在概念上就是当前应用状态的一个渲染函数,我们的事件处理程序(render) 本质上就是一个拥有特定 props 和 state 的特定渲染。但是,我们的 showMessage 回调并没有与任何一个特定的渲染“绑定”在一起,所以它“失去”了正确的 props。从 this 中读取数据的这种行为,切断了这种联系。
假如函数式组件不存在。我们将如何解决这个问题?
从问题的本质出发,因为异步等待的过程中然后切换用户,导致我们的 this 改变。
- 在回调调用之前就读取 this.props,然后保存下来。然后通过参数传递的方式,传递到回调函数中。
showMessage = (user) => {
alert('Followed ' + user);
};
handleClick = () => {
const {user} = this.props;
setTimeout(() => this.showMessage(user), 3000);
};
虽然能起作用,但是这种方法使得代码明显变得更加冗长,并且随着时间推移容易出错。如果我们需要的不止是一个props
怎么办?如果我们还需要访问state
怎么办?...
- 利用 JS 的闭包
简述一下闭包:因为JS的函数作用域是在声明时就确定了的,所以函数作用域内部的变量,不会受到外部的影响而销毁。只有当前函数执行完毕,才会释放整个作用域。
通常情况下,会避免使用闭包,因为会有内存泄漏,变量随时间推移而变化...等问题。但正也是因为React中,props和state是不可变的。这就消除了闭包的一个主要缺陷。
class ProfilePage extends React.Component {
render() {
// 你在渲染的时候就已经“捕获”了props:
const props = this.props;
// Note: we are *inside render*.
// These aren't class methods.
const showMessage = () => {
alert('Followed ' + props.user);
};
const handleClick = () => {
setTimeout(showMessage, 3000);
};
return <button onClick={handleClick}>Follow</button>;
}
}
嗯,上面的例子很正确,就是看起来很奇怪。我们在render方法中定义各种函数,而不是使用class的方法,那么使用类的意义在哪里?所以这里我们完全可以使用函数式组件了嘛。
通过删除类的“包裹”来简化代码:
function ProfilePage(props) {
const showMessage = () => {
alert('Followed ' + props.user);
};
const handleClick = () => {
setTimeout(showMessage, 3000);
};
return (
<button onClick={handleClick}>Follow</button>
);
}
就像上面这样,props 仍旧被捕获了 —— React将它们作为参数传递。不同于 this,props 对象本身永远不会被 React 改变。
函数式版本中,点击关注Sophie的账号,然后改变选择为Sunil仍旧会弹出'Followed Sophie'
网友评论