React函数组件与类的区别有哪些

 更新时间:2022年10月22日 10:21:44   作者:YinJie…  
函数式组件的基本意义就是,组件实际上是一个函数,不是类,下面这篇文章主要给大家介绍了关于React中函数组件与类的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下

首先我们要知道的是,项目性能能主要取决于代码的作用,而不是选择函数式还是类组件。尽管优化策略各有略微不同,但它们之间的性能差异可以忽略不计。

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

首先我们来看下面这个组件:

function App(props) {
  const showMessage = () => {
    alert('Hello' + props.user);
  };
  const handleClick = () => {
    setTimeout(showMessage, 3000);
  };
  return (
    <button onClick={handleClick}>Say</button>
  );
}

它渲染了一个利用来模拟网络请求,然后显示一个确认警告的按钮。例如,如果是传递进来的 props.user 是 jie,那么三秒后就会弹出 Hello jie。

那么我们用类应该怎么写这个组件呢?一个简单的重构可能就象这样:

class App extends React.Component {
  showMessage = () => {
    alert('Hello' + this.props.user);
  };
  handleClick = () => {
    setTimeout(this.showMessage, 3000);
  };
  render() {
    return <button onClick={this.handleClick}>Say</button>;
  }
}

我们通常做代码重构的时候都认为他们两个是等效的,但是事实真的如此吗,我们很少注意到它们之间的含义。

下面我们新建一个 react 项目,在 src下新建两个组件,一个 classComponent 组件,一个是 functionComponent 组件。代码就是上面我们写的这两个组件,只不过内容稍有区别:

classComponent:

import React from 'react';
class ProfilePage extends React.Component {
  showMessage = () => {
    alert('你选择了 ' + this.props.user);
  };
  handleClick = () => {
    setTimeout(this.showMessage, 3000);
  };
  render() {
    return <button onClick={this.handleClick}>选择</button>;
  }
}
export default ProfilePage;

functionComponent:

import React from 'react';
function ProfilePage(props) {
  const showMessage = () => {
    alert('你选择了 ' + props.user);
  };
  const handleClick = () => {
    setTimeout(showMessage, 3000);
  };
  return (
    <button onClick={handleClick}>选择</button>
  );
}
export default ProfilePage;

在 app.js 中我们将这两个组件引入:

import React from "react";
import ReactDOM from "react-dom";
import ProfilePageFunction from './functionComponent';
import ProfilePageClass from './classComponent';
export default class App extends React.Component {
  state = {
    user: '小杰',
  };
  render() {
    return (
      <>
        <label>
          <b>选择你想要拜访的朋友</b>
          <select
            value={this.state.user}
            onChange={e => this.setState({ user: e.target.value })}
          >
            <option value="小杰">小杰</option>
            <option value="小尚">小尚</option>
            <option value="小宁">小宁</option>
          </select>
        </label>
        <h1>欢迎来到 {this.state.user}的 家!</h1>
        <p>
          <ProfilePageFunction user={this.state.user} />
          <b> (这是来自函数式组件的)</b>
        </p>
        <p>
          <ProfilePageClass user={this.state.user} />
          <b> (这是来自类组件的)</b>
        </p>
      </>
    )
  }
}

运行项目,科研看到这样的界面:

当我们单击上面的按钮时,执行的就是函数式组件,点击下面的按钮时,执行的就是类。如果按照我们以往的思路,他们二者都会有相同的结果,但事实真的如此吗?

我们按照下面的顺序执行:

1. 点击函数式组件按钮

2. 在点击后立刻切换想要拜访的朋友

函数式组件的执行结果如下:

页面弹出的还是我们当时选择的值

同样的操作我们再试一下类组件:

现在页面弹出的就是我们实时更改的值了。

在这个例子中,第一个行为是正确的。因为最开始我选择要拜访小杰点击了确定发出了命令,然后我再切换到小尚,但是我并没有点击确定,我的组件不应该混淆我要拜访的人。在这里,类组件的实现很明显是错误的。

所以为什么我们的例子中类组件会有这样的表现?

让我们来仔细看看我们类组件中的方法:showMessage

showMessage = () => {
    alert('你选择了 ' + this.props.user);
  };

这个类方法从中读取数据。在 React 中 Props 是不可变的,所以他们永远不会改变。然而,this是,而且永远是,可变的。

事实上,这就是类组件存在的意义。React本身会随着时间的推移而改变,以便你可以在渲染方法以及生命周期方法中得到最新的实例。所以如果在请求已经发出的情况下我们的组件进行了重新渲染,将会改变。

我们的组件属于一个拥有特定 props 和 state 的特定渲染。

然而,调用一个回调函数读取 的 timeout 会打断这种关联。我们的回调并没有与任何一个特定的渲染绑定在一起,所以它失去了正确的 props。

二、闭包让类组件成为拥有特定props和state的渲染

我们想要以某种方式“修复”拥有正确 props 的渲染与读取这些 props 的回调之间的联系。它们在类的某个地方被弄丢了。

一种方法是在调用事件之前读取,然后将他们显式地传递到timeout回调函数中去:

import React from 'react';
class ProfilePage extends React.Component {
  showMessage = (user) => {
    alert('你选择了 ' + user);
  };
  handleClick = () => {
    const {user} = this.props;
    setTimeout(() => this.showMessage(user), 3000);
  };
  render() {
    return <button onClick={this.handleClick}>确定</button>;
  }
}
export default ProfilePage;

这种方法会起作用。然而,这种方法使得代码明显变得更加冗长,并且随着时间推移容易出错。如果我们需要的不止是一个props怎么办?如果我们还需要访问state怎么办?

然而,如果我们能利用JavaScript闭包的话问题将迎刃而解。

通常来说我们会避免使用闭包,但是在React中,props和state是不可变的,这就消除了闭包的一个主要缺陷。

这就意味着如果你在一次特定的渲染中捕获那一次渲染所用的props或者state,你会发现他们总是会保持一致,就如同你的预期那样。

class ProfilePage extends React.Component {
  render() {
    const props = this.props;
    const showMessage = () => {
      alert('你选择了 ' + props.user);
    };
    const handleClick = () => {
      setTimeout(showMessage, 3000);
    };
    return <button onClick={handleClick}>确定</button>;
  }
}

你在渲染的时候就已经“捕获”了props。这样,在它内部的任何代码(包括)都保证可以得到这一次特定渲染所使用的props。上面的例子是正确的,但是看起来很奇怪。如果你在方法中定义各种函数,而不是使用class的方法,那么使用类的意义在哪里?

所以这个时候我们就明白了函数式组件和类组件的区别:

function ProfilePage({ user }) {
  const showMessage = () => {
    alert('Followed ' + user);
  };
  const handleClick = () => {
    setTimeout(showMessage, 3000);
  };
  return (
    <button onClick={handleClick}>Follow</button>
  );
}

当父组件使用不同的props来渲染时,React会再次调用函数。但是我们点击的事件处理函数,"属于"具有自己的值的上一次渲染,并且回调函数也能读取到这个值。它们都保持完好无损。

三、区分useState与useRef的使用

使用Hooks,同样的原则也适用于状态。看这个例子:

function MessageThread() {
  const [message, setMessage] = useState('');
  const showMessage = () => {
    alert('You said: ' + message);
  };
  const handleSendClick = () => {
    setTimeout(showMessage, 3000);
  };
  const handleMessageChange = (e) => {
    setMessage(e.target.value);
  };
  return (
    <>
      <input value={message} onChange={handleMessageChange} />
      <button onClick={handleSendClick}>Send</button>
    </>
  );
}

如果我发送一条特定的消息,组件不应该对实际发送的是哪条消息感到困惑。这个函数组件的变量捕获了我们在浏览器中执行单击处理函数的那一次渲染。所以当我点击“发送”时那一刻输入框中的内容就会被设置为弹出的值。

因此我们知道,在默认情况下React中的函数会捕获props和state。但是如果我们想要读取并不属于这一次特定渲染的,最新的props和state呢?

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

function MyComponent() {
  const ref = useRef(null);
  // 你可以通过 ref.current 来获取保存的值.
  // ...
}

在很多情况下,你并不需要它们,并且分配它们将是一种浪费。但是,如果你愿意,你可以这样手动地来追踪这些值:

function MessageThread() {
  const [message, setMessage] = useState('');
  const latestMessage = useRef('');
  const showMessage = () => {
    alert('You said: ' + latestMessage.current);
  };
  const handleSendClick = () => {
    setTimeout(showMessage, 3000);
  };
  const handleMessageChange = (e) => {
    setMessage(e.target.value);
    latestMessage.current = e.target.value;
  };

如果我们在 state 中读取,我们将得到在我们按下发送按钮那一刻的信息。但是当我们通过 ref 读取时,我们将得到最新的值,即使我们在按下发送按钮后继续输入。

通常情况下,你应该避免在渲染期间读取或者设置refs,因为它们是可变得。我们希望保持渲染的可预测性。然而,如果我们想要特定props或者state的最新值,那么手动更新ref会有些烦人。我们可以通过使用一个effect来自动化实现它:

function MessageThread() {
  const [message, setMessage] = useState('');
  // 保持追踪最新的值。
  const latestMessage = useRef('');
  useEffect(() => {
    latestMessage.current = message;
  });
  const showMessage = () => {
    alert('You said: ' + latestMessage.current);
  };

正如我们上面看到的,闭包实际上帮我们解决了很难注意到的细微问题。同样,它们也使得在并发模式下能更轻松地编写能够正确运行的代码。这是可行的,因为组件内部的逻辑在渲染它时捕获并包含了正确的props和state。

React函数总是捕获他们的值 —— 现在我们也知道这是为什么了。

到此这篇关于React函数组件与类的区别有哪些的文章就介绍到这了,更多相关React函数组件与类内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • react+redux的升级版todoList的实现

    react+redux的升级版todoList的实现

    本篇文章主要介绍了react+redux的升级版todoList的实现,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-12-12
  • 在react中使用vuex的示例代码

    在react中使用vuex的示例代码

    这篇文章主要介绍了在react中使用vuex的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-07-07
  • 关于react-router-dom路由入门教程

    关于react-router-dom路由入门教程

    这篇文章主要介绍了关于react-router-dom路由入门教程,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • React Suspense解决竞态条件详解

    React Suspense解决竞态条件详解

    这篇文章主要为大家介绍了React Suspense解决竞态条件详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • React反向代理及样式独立详解

    React反向代理及样式独立详解

    这篇文章主要介绍了React反向代理及样式独立详解,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的朋友可以参考一下
    2022-08-08
  • 如何在 React 中调用多个 onClick 函数

    如何在 React 中调用多个 onClick 函数

    这篇文章主要介绍了如何在React中调用多个onClick函数,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2023-11-11
  • react中context传值和生命周期详解

    react中context传值和生命周期详解

    这篇文章主要介绍了react中context传值和生命周期,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-03-03
  • React组件的创建与state同步异步详解

    React组件的创建与state同步异步详解

    这篇文章主要介绍了react组件实例属性state,有状态state的组件称作复杂组件,没有状态的组件称为简单组件,状态里存储数据,数据的改变驱动页面的展示,本文结合实例代码给大家详细讲解,需要的朋友可以参考下
    2023-03-03
  • react echarts tooltip 区域新加输入框编辑保存数据功能

    react echarts tooltip 区域新加输入框编辑保存数据功能

    这篇文章主要介绍了react echarts tooltip 区域新加输入框编辑保存数据功能,大概思路是用一个div包裹echarts, 然后在echarts的同级新建一个div用来用来模拟真实tooltip,通过鼠标移入移出事件控制真实tooltip的显示与隐藏,需要的朋友可以参考下
    2023-05-05
  • React如何以Hook的方式使用Echarts

    React如何以Hook的方式使用Echarts

    这篇文章主要介绍了React如何以Hook的方式使用Echarts问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-03-03

最新评论