一篇对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. Codeforces Round #305 (Div. 1) B. Mike and Feet

    Mike is the president of country What-The-Fatherland. There are n bears living in this country besid ...

  2. Codeforces Round #494 (Div. 3) D. Coins and Queries (贪心,数学)

    题意:给你一组全是\(2^d\ (d\ge0)\)的数,询问q次,每次询问一个数,问这个数是否能够由原数组中的数相加得到,如果能,输出最少用多少个数,否则输出\(-1\). 题解:首先贪心得出结论:如 ...

  3. Codeforces Round #653 (Div. 3) D. Zero Remainder Array (数学,模拟)

    题意:有一组数,刚开始时\(x=0\),每次可以让\(x\)++或让某一个元素+=\(x\)后\(x\)++,每个元素只能加一次\(x\),问最少操作多少次使得所有元素能被\(k\)整除. 题解:每个 ...

  4. 数理统计10(习题篇):寻找UMVUE

    利用L-S定理,充分完备统计量法是寻找UMVUE的最方便方法,不过实际运用时还需要一些小技巧,比如如何写出充分完备统计量.如何找到无偏估计.如何求条件期望,等等.课本上的例题几乎涵盖了所有这些技巧,我 ...

  5. oslab oranges 一个操作系统的实现 实验一

    实验目的: 搭建基本实验环境,熟悉基本开发与调试工具 对应章节:第一.二章 实验内容: 1.认真阅读章节资料 2.在实验机上安装virtualbox,并安装ubuntu 3.安装ubuntu开发环境, ...

  6. javascript & call & apply & bind & new

    javascript & call & apply & bind & new Javascript call() & apply() vs bind()? ht ...

  7. 十三香 & 香料

    十三香 & 香料 十三香原料组成不完全一致, 但有一些香料却是大家都会采用的: 草蔻.砂仁.肉豆蔻.肉桂.丁香. 花椒.大料.小茴香.木香.白芷. 山萘.良姜和姜 王守义十三香 http:// ...

  8. html fragment & html template & virtual DOM & web components

    html fragment & html template & virtual DOM https://developer.mozilla.org/en-US/docs/Web/API ...

  9. react 遍历 object

    @observable obj = { name: "ajanuw", age: 22, }; @computed get list() { return _.toPairs(th ...

  10. [转]hpp.h与.h的区别

    原文网址:https://blog.csdn.net/liuzhanchen1987/article/details/7270005 hpp,其实质就是将.cpp的实现代码混入.h头文件当中,定义与实 ...