一篇对DanHow Are Function Components Different from Classes? 一文的个人阅读总结,内容来自于此。强烈推荐阅读 Dan Abramov.的博客

函数式组件和Class组件有什么不同?

Dan很直接的给出了答案:

函数式组件捕获了渲染所用的值。(Function components capture the rendered values.)

直接看结论可能有点不知所云。

class组件可能引发的"错误"

看一个组件,使用setTimeout模拟网络请求,点击button之后警告提示关注某人(user),userprops中读取。

该组件的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),三秒之后的弹出框是followuserB。(这个动作会在后面多次提及)

在选中userA的时候点击关注,目的就是关注userA,但是class组件最后弹出框显示的关注userB,这显然不符合预期。

为什么、如何解决

如果上面例子使用function组件,弹出框显示的就会是正确的,虽然切换到了选中的userB,但是弹出框显示的仍然是点击的那一刻关注的userA

function组件好像记下了点击那一刻时候的状态?

class组件在这个场景中错误的原因是class组件每次三秒后从this.props.user中读取数据,此时的this.props.user已经变了,已经是切换后的新的this.props.user数据。虽然React中props不可变,但是this是可变的

类组件会随着时间推移改变,在渲染方法和生命周期方法中得到的是最新的实例。而函数式组件的事件处理程序就是渲染结果的一部分,事件处理程序属于一个拥有特定的propsstate的渲染

也就是说函数式组件保持了事件处理程序与那一次渲染propsstate之间的联系,本身就是正确的。


来修复类组件中的这个问题:

  1. 可以在点击的时候就读取并记录当下的stateprops,三秒后读取记录的数据(而不是读this.props.xx)再弹出。

    方案可行但是扩展极差,在其他多个变量也这样做的时候逐层记录或传递非常繁复。

  2. 闭包。

    闭包维持了一个可能随时间变化的变量,而此处我们要维持的是React的propsstate,React设计中这都是不可变的。让闭包来维持不变的stateprops,此时再去捕获这些值,就是一致的。

    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值的一次渲染,事件处理函数和其他回调函数也能读到这个值。

回头看这个结论,是不是更好理解一点了:

函数式组件捕获了渲染所使用的值

函数式组件使用最新的propsstate

函数式组件捕获了特定渲染的propsstate。但是我们如果又想和class组件一样读取最新的propsstate呢?

useRef

Dan 老师:在函数式组件中,你也可以拥有一个在所有的组件渲染帧中共享的可变变量。它被成为“ref”

this.something就像是something.current的一个镜像。他们代表了同样的概念。

每一次的渲染结果可以视为一个渲染帧,共享的变量设置为ref,包含DOMRefclass中的实例变量的功能,可以说是非常强大了。

需要最新的propsstate值,可以使用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]的更多相关文章

  1. React - 组件:类组件

    目录: 1. 类组件有自己的状态 2. 继承React.Component-会有生命周期和this 3. 内部需要一个render函数(类组件会默认调用render方法,但不会默认添加,需要手动填写r ...

  2. React Native知识5-Touchable类组件

    React Native 没有像web那样可以给元素绑定click事件,前面我们已经知道Text组件有onPress事件,为了给其他组件 也绑定点击事件,React Native提供了3个组件来做这件 ...

  3. React函数类组件及其Hooks学习

    目录 函数类组件 函数式组件和类式组件的区别: 为什么要使用函数式组件? Hooks概念及常用的Hooks 1. useState: State的Hook 语法 useState()说明: setXx ...

  4. React中的高阶组件,无状态组件,PureComponent

    1. 高阶组件 React中的高阶组件是一个函数,不是一个组件. 函数的入参有一个React组件和一些参数,返回值是一个包装后的React组件.相当于将输入的React组件进行了一些增强.React的 ...

  5. React 深入系列2:组件分类

    文:徐超,<React进阶之路>作者 授权发布,转载请注明作者及出处 React 深入系列2:组件分类 React 深入系列,深入讲解了React中的重点概念.特性和模式等,旨在帮助大家加 ...

  6. 【React】react学习笔记02-面向组件编程

    react学习笔记02-面向组件编程 面向组件编程,直白来说,就是定义组件,使用组件. 以下内容则简单介绍下组建的声明与使用,直接复制demo观测结果即可. 步骤: 1.定义组件   a.轻量组件-函 ...

  7. 如何对 React 函数式组件进行优化

    文章首发个人博客 前言 目的 本文只介绍函数式组件特有的性能优化方式,类组件和函数式组件都有的不介绍,比如 key 的使用.另外本文不详细的介绍 API 的使用,后面也许会写,其实想用好 hooks ...

  8. React函数式组件使用Ref

    目录: 简介 useRef forwardRef useImperativeHandle 回调Ref 简介 大家都知道React中的ref属性可以帮助我们获取子组件的实例或者Dom对象,进而对子组件进 ...

  9. React函数式组件的性能优化

    优化思路 主要优化的方向有2个: 减少重新 render 的次数.因为在 React 里最重(花时间最长)的一块就是 reconction(简单的可以理解为 diff),如果不 render,就不会 ...

随机推荐

  1. 【noi 2.6_6049】买书(DP)

    题意:有N元,有无限多本10.20.50和100元的书,问有几种购买方案. 解法:f[i]表示用 i 元的方案数.还有一个 j 循环这次买多少元的书. 注意--要先 j 循环,再 i 循环.因为要先考 ...

  2. js实现一棵树的生长

    参考链接:https://blog.csdn.net/u010298576/article/details/76609244 HTML网页源码: 1 <!DOCTYPE html> 2 & ...

  3. 在程序中通过Process启动外部exe的方法及注意事项

    启动外部进程的方法: /// <summary> /// 启动外部进程 /// </summary> /// <param name="path"&g ...

  4. AbstractQueuedSynchronizer解析

    AbstractQueuedSynchronizer简称为AQS,是juc里很基本的一个包,juc里很多工具类是基于AQS实现的,理解了AQS,其它很多juc工具类也会比较清楚了. 1.方法简述 ge ...

  5. 计量经济学导论10:ARIMA模型

    目录 ${\rm ARIMA}$ 模型 滞后算子 ${\rm MA}(q)$ 模型 ${\rm MA}(1)$ 模型 ${\rm MA}(q)$ 模型 ${\rm AR}(p)$ 模型 ${\rm A ...

  6. npm publish & 403 Forbidden

    npm publish & 403 Forbidden 403 Forbidden - PUT https://registry.npmjs.org/ https://www.npmjs.co ...

  7. iPhone 12 导入通讯录排序 Bug

    iPhone 12 导入通讯录排序 Bug iOS iOS 通讯录排序问题 Huawei OK solution iOS 切换中英文,修复排序通讯录 bug Awesome iOS Contacts ...

  8. web components in action

    web components in action web components css-doodle.js https://alligator.io/workflow/ https://d33wubr ...

  9. javascript nested object merge

    javascript nested object merge deep copy Object reference type function namespace(oNamespace, sPacka ...

  10. ES-Next & ECMAScript 2019

    ES-Next & ECMAScript 2019 Data Types 8 种 js 基本数据类型 8 zhon The latest ECMAScript standard defines ...