React函数式组件和类组件[Dan]
一篇对
Dan
的 How Are Function Components Different from Classes? 一文的个人阅读总结,内容来自于此。强烈推荐阅读 Dan Abramov.的博客。
函数式组件和Class组件有什么不同?
Dan
很直接的给出了答案:
函数式组件捕获了渲染所用的值。(Function components capture the rendered values.)
直接看结论可能有点不知所云。
class
组件可能引发的"错误"
看一个组件,使用setTimeout
模拟网络请求,点击button
之后警告提示关注某人(user
),user
从props
中读取。
该组件的function
版本:
function ProfilePage(props) {
const showMessage = () => {
alert('Followed ' + props.user);
};
const handleClick = () => {
setTimeout(showMessage, 3000);
};
return (
<button onClick={handleClick}>Follow</button>
);
}
class
版本:
class ProfilePage extends React.Component {
showMessage = () => {
alert('Followed ' + this.props.user);
};
handleClick = () => {
setTimeout(this.showMessage, 3000);
};
render() {
return <button onClick={this.handleClick}>Follow</button>;
}
}
页面组件代码:
class App extends React.Component {
state = {
user: 'Dan',
};
render() {
return (
<>
<label>
<b>Choose profile to view: </b>
<select
value={this.state.user}
onChange={e => this.setState({ user: e.target.value })}
>
<option value="Dan">Dan</option>
<option value="Sophie">Sophie</option>
<option value="Sunil">Sunil</option>
</select>
</label>
<h1>Welcome to {this.state.user}’s profile!</h1>
<p>
<ProfilePageFunction user={this.state.user} />
<b> (function)</b>
</p>
<p>
<ProfilePageClass user={this.state.user} />
<b> (class)</b>
</p>
<p>
Can you spot the difference in the behavior?
</p>
</>
)
}
}
对于class
组件,在选中状态是userA
的时候,点击follow button
之后立马将select
切换其他人(uerB
),三秒之后的弹出框是follow
的userB
。(这个动作会在后面多次提及)
在选中userA
的时候点击关注,目的就是关注userA
,但是class
组件最后弹出框显示的关注userB
,这显然不符合预期。
为什么、如何解决
如果上面例子使用function
组件,弹出框显示的就会是正确的,虽然切换到了选中的userB
,但是弹出框显示的仍然是点击的那一刻关注的userA
。
function
组件好像记下了点击那一刻时候的状态?
class
组件在这个场景中错误的原因是class
组件每次三秒后从this.props.user
中读取数据,此时的this.props.user
已经变了,已经是切换后的新的this.props.user
数据。虽然React中props
不可变,但是this
是可变的。
类组件会随着时间推移改变,在渲染方法和生命周期方法中得到的是最新的实例。而函数式组件的事件处理程序就是渲染结果的一部分,事件处理程序属于一个拥有特定的props
和state
的渲染。
也就是说函数式组件保持了事件处理程序与那一次渲染props
的state
之间的联系,本身就是正确的。
来修复类组件中的这个问题:
可以在点击的时候就读取并记录当下的
state
或props
,三秒后读取记录的数据(而不是读this.props.xx
)再弹出。方案可行但是扩展极差,在其他多个变量也这样做的时候逐层记录或传递非常繁复。
闭包。
闭包维持了一个可能随时间变化的变量,而此处我们要维持的是React的
props
或state
,React设计中这都是不可变的。让闭包来维持不变的state
和props
,此时再去捕获这些值,就是一致的。在
render
函数中使用闭包:class ProfilePage extends React.Component {
render() {
// Capture the 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>;
}
}
渲染的时候这些需要使用的
props
已经被捕获(就像上面方案1
的记录,在render
的时候就已经读取记录下了)。此时表现弹出内容就会是点击时候的那个userA
了。
class
组件的这个问题是修复了,但是在render
函数中添加那么多的函数,且并没有挂载到class
上,有点奇怪?
其实去掉class
,这就是函数式组件的形式了:
function ProfilePage(props) {
const showMessage = () => {
alert('Followed ' + props.user);
};
const handleClick = () => {
setTimeout(showMessage, 3000);
};
return (
<button onClick={handleClick}>Follow</button>
);
}
React
将他们作为参数传递,props
在渲染时被捕获了。不同于class
组件的this
,这里的props
不会被改变。
点击事件处理函数,该函数属于具有正确user
值的一次渲染,事件处理函数和其他回调函数也能读到这个值。
回头看这个结论,是不是更好理解一点了:
函数式组件捕获了渲染所使用的值
函数式组件使用最新的props
和state
函数式组件捕获了特定渲染的props
和state
。但是我们如果又想和class
组件一样读取最新的props
和state
呢?
useRef
Dan 老师:在函数式组件中,你也可以拥有一个在所有的组件渲染帧中共享的可变变量。它被成为“ref”
this.something
就像是something.current
的一个镜像。他们代表了同样的概念。
每一次的渲染结果可以视为一个渲染帧,共享的变量设置为ref
,包含DOMRef
和class
中的实例变量的功能,可以说是非常强大了。
需要最新的props
和state
值,可以使用useRef
创建的变量来记录,通过useEffect
可以在值变化的时候自动追踪。
function MessageThread() {
const [message, setMessage] = useState('');
// 保持追踪最新的值。
const latestMessage = useRef('');
useEffect(() => {
latestMessage.current = message;
});
const showMessage = () => {
alert('You said: ' + latestMessage.current);
};
React函数总是捕获他们的值。
React函数式组件和类组件[Dan]的更多相关文章
- React - 组件:类组件
目录: 1. 类组件有自己的状态 2. 继承React.Component-会有生命周期和this 3. 内部需要一个render函数(类组件会默认调用render方法,但不会默认添加,需要手动填写r ...
- React Native知识5-Touchable类组件
React Native 没有像web那样可以给元素绑定click事件,前面我们已经知道Text组件有onPress事件,为了给其他组件 也绑定点击事件,React Native提供了3个组件来做这件 ...
- React函数类组件及其Hooks学习
目录 函数类组件 函数式组件和类式组件的区别: 为什么要使用函数式组件? Hooks概念及常用的Hooks 1. useState: State的Hook 语法 useState()说明: setXx ...
- React中的高阶组件,无状态组件,PureComponent
1. 高阶组件 React中的高阶组件是一个函数,不是一个组件. 函数的入参有一个React组件和一些参数,返回值是一个包装后的React组件.相当于将输入的React组件进行了一些增强.React的 ...
- React 深入系列2:组件分类
文:徐超,<React进阶之路>作者 授权发布,转载请注明作者及出处 React 深入系列2:组件分类 React 深入系列,深入讲解了React中的重点概念.特性和模式等,旨在帮助大家加 ...
- 【React】react学习笔记02-面向组件编程
react学习笔记02-面向组件编程 面向组件编程,直白来说,就是定义组件,使用组件. 以下内容则简单介绍下组建的声明与使用,直接复制demo观测结果即可. 步骤: 1.定义组件 a.轻量组件-函 ...
- 如何对 React 函数式组件进行优化
文章首发个人博客 前言 目的 本文只介绍函数式组件特有的性能优化方式,类组件和函数式组件都有的不介绍,比如 key 的使用.另外本文不详细的介绍 API 的使用,后面也许会写,其实想用好 hooks ...
- React函数式组件使用Ref
目录: 简介 useRef forwardRef useImperativeHandle 回调Ref 简介 大家都知道React中的ref属性可以帮助我们获取子组件的实例或者Dom对象,进而对子组件进 ...
- React函数式组件的性能优化
优化思路 主要优化的方向有2个: 减少重新 render 的次数.因为在 React 里最重(花时间最长)的一块就是 reconction(简单的可以理解为 diff),如果不 render,就不会 ...
随机推荐
- 2019牛客暑期多校训练营(第七场)B Irreducible Polynomial
传送门 题意: 给你一个n次n+1项式(An*X^n+A(n-1)*X^(n-1)...A*X+A0),将系数An都给你,问你这个多项式是不是一个不可约多项式,可约多项式就是类型(x+1)*(x+2) ...
- POJ2429 GCD & LCM Inverse pollard_rho大整数分解
Given two positive integers a and b, we can easily calculate the greatest common divisor (GCD) and t ...
- Codeforces Round #669 (Div. 2) C. Chocolate Bunny (交互,构造)
题意:有一个长度为\(n\)的隐藏序列,你最多可以询问\(2n\)次,每次可以询问\(i\)和\(j\)位置上\(p[i]\ mod\ p[j]\)的结果,询问的格式是\(?\ x\ y\),如果已经 ...
- 爬虫入门五 gooseeker
title: 爬虫入门五 gooseeker date: 2020-03-16 16:00:00 categories: python tags: crawler gooseeker是一个简单的爬虫软 ...
- 记录一个状压DP用到的骚操作
不断的让i=i^lowbit(i)就可以枚举i二进制里面所有的1 嘛,很显然,怕是我没想到哦
- 重新上手c语言的一些坑
c语言结构体不能声明函数,放几个函数指针倒是没问题 c语言结构体不能在声明时初始化 声明两个指针 int *a,*b; 或者typedef int* int_P int_P a,b; typedef要 ...
- AbstractQueuedSynchronizer解析
AbstractQueuedSynchronizer简称为AQS,是juc里很基本的一个包,juc里很多工具类是基于AQS实现的,理解了AQS,其它很多juc工具类也会比较清楚了. 1.方法简述 ge ...
- Java 对象的哈希值是每次 hashCode() 方法调用重计算么?
对于没有覆盖hashCode()方法的对象 如果没有覆盖 hashCode() 方法,那么哈希值为底层 JDK C++ 源码实现,实例每次调用hashcode()方法,只有第一次计算哈希值,之后哈希值 ...
- Array in Depth
Array in Depth Array.concat() & Array.push() https://developer.mozilla.org/en-US/docs/Web/JavaSc ...
- RT-Thread学习笔记3-线程间通信 & 定时器
目录 1. 事件集的使用 1.1 事件集控制块 1.2 事件集操作 2. 邮箱的使用 2.1 邮箱控制块 2.2 邮箱的操作 3. 消息队列 3.1 消息队列控制块 3.2 消息队列的操作 4. 软件 ...