一、前言

react组件分为类(class)组件和函数(function)组件。

class 组件是通过继承模版类(Component、PureComponent)的方式开发新组件的,继承是 class 本身的特性,它支持设置 state,会在 state 改变后重新渲染,可以重写一些父类的方法,这些方法会在 React 组件渲染的不同阶段调用,叫做生命周期函数。

function 组件不能做继承,因为 function 本来就没这个特性,并且函数式组件也没有生命周期,所以react提供了一些 api 供函数来弥补函数组件的缺陷,这些 api 会在内部的一个数据结构上挂载一些函数和值,并执行相应的逻辑,通过这种方式实现了 state 和类似 class 组件的生命周期函数的功能,这种 api 就叫做 hooks

二、React Hooks

其实我们已经使用过一些hooks了,例如useState来实现响应式的数据功能,使用useEffect实现类组件才拥有的组件生命周期,但是都没有详细地研究其参数和使用。

2.1 useState

state保存着当前组件的状态,当我们改变state,页面ui就会自动更新,及实现了响应式。

类组件中使用state:

函数组件需要借助useState

用法:

const [stateName, setStateName] = React.useState(stateValue);

以下代码实现了一秒后页面上的文字变化:

function MyComponent() {
  const [msg, setMsg] = React.useState("Hello React!");
  setTimeout(() => setMsg("Hello Hooks!"), 1000);
  return <p>{msg}</p>;
}
ReactDOM.render(<MyComponent />, document.getElementById("app"));

效果:

2.2 useEffect

React hooks也提供了 api ,用于弥补函数组件没有生命周期的缺陷。

用法:

useEffect(()=>{
    return destory
},dep)

useEffect 第一个参数 是一个函数, 返回的 destory , destory 作为下一次callback执行之前调用,用于清除上一次 回调函数产生的副作用。

第二个参数作为依赖项,是一个数组,可以有多个依赖项,依赖项改变,执行上一次回调函数返回的 destory ,和执行新的 effect 第一个参数。

对于 useEffect 执行, React 处理逻辑是采用异步调用 ,对于每一个 effect 的 callback, React 会向 setTimeout回调函数一样,放入任务队列,等到主线程任务完成,DOM 更新,js 执行完成,视图绘制完毕,才执行。所以 effect 回调函数不会阻塞浏览器绘制视图。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>useEffect</title>
    <script src="https://cdn.staticfile.org/react/16.8.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.8.0/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 -->
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
  </head>
  <body>
    <div id="app"></div>
    <script type="text/babel">
      const Clock = (props) => {
        const [n, setN] = React.useState(0);
        function addNum() {
          setN(n   1);
        }
        React.useEffect(() => {
          console.log(n);
        });
        return (
          <div>
            <p>现在的n是 {n} .</p>
            <button onClick={addNum}>n 1</button>
          </div>
        );
      };
      ReactDOM.render(<Clock />, document.getElementById("app"));
    </script>
  </body>
</html>

  • 组合 componentDidMount componentDidUpdate

当useEffect没有第二个参数时,组件的初始化和更新都会执行。

 useEffect(() => {
    //doSomething
  }, [])
  • componentDidMount

useEffect的第二个参数为一个空数组,初始化调用一次之后不再执行,相当于componentDidMount

 useEffect(() => {
    //doSomething
  }, [])
  • 组合 componentDidMount componentWillUnmount

useEffect返回一个函数,这个函数会在组件卸载时执行。

useEffect(() => {
    return () => {
    	...
    };
  }, []);

2.3 useMemo

它返回的是一个 memoized 值,仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。我们可以拿 vue 里面的 计算属性computed和它做一个对比,都是返回基于依赖重新计算的值。

const cacheSomething = useMemo(create,deps)

① create:第一个参数为一个函数,函数的返回值作为缓存值。

② deps: 第二个参数为一个数组,存放当前 useMemo 的依赖项,在函数组件下一次执行的时候,会对比 deps 依赖项里面的状态,是否有改变,如果有改变重新执行 create ,得到新的缓存值。

③ acheSomething:返回值,执行 create 的返回值。如果 deps 中有依赖项改变,返回的重新执行 create 产生的值,否则取上一次缓存值。

function MyComponent() {
	 const [n, setN] = React.useState(0);
	 const [t, setT] = React.useState(0);
	 const getT = () => {
	   console.log("调用getT获取T!");
	    return t;
	  };
	 return (
	   <div>
	     <p>n: {n}</p>
	     <p>t: {getT}</p>
	     <button onClick={() => setN(n   1)}>n   1</button>
	     <br />
	     <button onClick={() => setT(t   1)}>t   1</button>
	   </div>
	 );
}
ReactDOM.render(<MyComponent />, document.getElementById("app"));

此时无论改变n还是t,都会导致getT函数被调用,但是事实上,在n改变时,并不需要重新调用一遍getT函数。

我们使用useMemo来实现仅t改变,getT才重新运行。

      function MyComponent() {
        const [n, setN] = React.useState(0);
        const [t, setT] = React.useState(0);
        const getT = React.useMemo(() => {
          console.log("调用getT获取T!");
          return t;
        }, [t]);
        return (
          <div>
            <p>n: {n}</p>
            <p>t: {getT}</p>
            <button onClick={() => setN(n   1)}>n   1</button>
            <br />
            <button onClick={() => setT(t   1)}>t   1</button>
          </div>
        );

注意,此时getT不在是一个函数,而是useMemo回调函数返回的值,所以在页面上直接使用getT即可。

2.4 useCallback

useMemo 和 useCallback 接收的参数都是一样,都是在其依赖项发生变化后才执行,都是返回缓存的值。

区别在于 useMemo 返回的是函数运行的结果,useCallback 返回的是函数,这个回调函数是经过处理后的也就是说父组件传递一个函数给子组件的时候。

由于是无状态组件每一次都会重新生成新的 props 函数,这样就使得每一次传递给子组件的函数都发生了变化,这时候就会触发子组件的更新,这些更新是没有必要的,此时我们就可以通过 usecallback 来处理此函数,然后作为 props 传递给子组件。

我们依然利用useMemo的例子:

可以看到,此时getT是一个函数,并不是一个值,这也是useMemo和useCallback的区别,即返回结果不同。

2.5 useContext

可以使用 useContext ,来获取父级组件传递过来的 context 值(上下文),这个当前值就是最近的父级组件 Provider 设置的 value 值,useContext 参数一般是由 createContext 方式创建的 ,也可以父级上下文 context 传递的 ( 参数为 context )。

创建context:

const context = createContext();

使用context:

const contextValue = useContext(context)
const Context = React.createContext();
const ChildComponent01 = () => {
  const c = React.useContext(Context);
  return (
    <div>
      <p>/* 用useContext方式 */</p>
      My name is {c.name}, I'm {c.age}.
    </div>
  );
};
const ChildComponent02 = () => {
  return (
    <Context.Consumer>
      {(value) => (
        <div>
          <p>/* 用Context.Consumer 方式 */</p>
          My name is {value.name}, I'm {value.age}.
        </div>
      )}
    </Context.Consumer>
  );
};
const MyComponent = () => {
  return (
    <div>
      <Context.Provider value={{ name: "yancy", age: 20 }}>
        <ChildComponent01 />
        <ChildComponent02 />
      </Context.Provider>
    </div>
  );
}
ReactDOM.render(<MyComponent />, document.getElementById("app"));

使用useContext可以避免使用props进行传参造成数据传递十分繁琐和困难的问题。

2.6 useRef

在 React 数据流中,props 是父组件和子组件交互的唯一方式。要修改一个子组件,必须使用新的 props 去重新渲染它。而 refs 提供了另一种方式,允许我们在 React 典型数据流之外,去操作 DOM 元素和类组件的实例。

useRef 返回一个对象,返回的ref对象在组件的整个生命周期保持不变。

最常用的ref是两种对象
用法1: 引入DOM(或者组件,组件必须是类组件)元素
用饭2: 保存一个数据,这个数据在组件的整个生命周期中可以保存

基于组件的框架(react、vue)是不推荐直接操作dom元素的,例如使用document.getElementById(“”)来获取元素,react提供了useRef来使我们能够获取绑定的dom元素。

const MyComponent = () => {
        const titleRef = React.useRef();
        const inputRef = React.useRef();
        function changeDOM() {
          titleRef.current.innerHTML = "useRef";
          inputRef.current.focus();
        }
        return (
          <div>
            <h2 ref={titleRef}> Hello World </h2>
            <input ref={inputRef} type="text" />
            <button onClick={(e) => changeDOM()}>修改DOM</button>
          </div>
        );
      };
      ReactDOM.render(<MyComponent />, document.getElementById("app"));

还可以利用useRef配合useEffect来获取上一次的state:

const MyComponent = () => {
  const [count, setCount] = React.useState(0);
  const numRef = React.useRef(count);
  React.useEffect(() => {
    numRef.current = count;
  }, [count]);
  return (
    <div>
      <h2>count 上一次的值: {numRef.current}</h2>
      <h2>count 这一次的值: {count}</h2>
      <button onClick={(e) => setCount(count   1)}>count   1</button>
    </div>
  );
};
ReactDOM.render(<MyComponent />, document.getElementById("app"));

三、总结

在 react 的 class组件写法中,随处可见各种各样的 .bind(this)。(甚至官方文档里也有专门的章节描述了“为什么绑定是必要的?”这一问题)

而在函数组件中通过使用hooks,可以完美地代替类组件,并且几乎不用关心this的指向问题。

Vue3.2的组合式api也引入了hooks,可以看到hooks是前端框架发展的趋势,是一个组件的灵魂所在。

到此这篇关于React组件学习之Hooks使用的文章就介绍到这了,更多相关React Hooks内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

React组件学习之Hooks使用的更多相关文章

  1. ios – React native链接到另一个应用程序

    如果是错误的,有人知道如何调用正确的吗?

  2. ios – React Native – 在异步操作后导航

    我正在使用ReactNative和Redux开发移动应用程序,我正面临着软件设计问题.我想调用RESTAPI进行登录,如果该操作成功,则导航到主视图.我正在使用redux和thunk所以我已经实现了异步操作,所以我的主要疑问是:我应该把逻辑导航到主视图?我可以直接从动作访问导航器对象并在那里执行导航吗?.我对组件中的逻辑没有信心.似乎不是一个好习惯.有没有其他方法可以做到这一点?

  3. 在ios中使用带有React Native(0.43.4)的cocoapods的正确方法是什么?

    我已经挖掘了很多帖子试图使用cocoapods为本地ios库设置一个反应原生项目,但我不可避免地在#import中找到了丢失文件的错误.我的AppDelegate.m文件中的语句.什么是使用反应原生的可可豆荚的正确方法?在这篇文章发表时,我目前的RN版本是0.43.4,而我正在使用Xcode8.2.1.这是我的过程,好奇我可能会出错:1)

  4. ios – React Native WebView滚动行为无法按预期工作

    如何确保滚动事件的行为与ReactNative应用程序中的浏览器相同?

  5. ios – React Native – BVLinearGradient – 找不到’React/RCTViewManager.h’文件

    谢谢.解决方法几天前我遇到了完全相同的问题.问题是在构建应用程序时React尚未链接.试试这个:转到Product=>Scheme=>管理方案…=>点击你的应用程序Scheme,然后点击Edit=>转到Build选项卡=>取消选中ParallelizeBuild然后点击标志添加目标=>搜索React,选择第一个名为React的目标,然后单击Add然后在目标列表中选择React并将其向上拖动到该列表中的第一个.然后转到Product=>再次清理并构建项目.这应该有所帮助.

  6. ios – React Native – NSNumber无法转换为NSString

    解决方法在你的fontWeight()函数中也许变成:

  7. ios – React native error – react-native-xcode.sh:line 45:react-native:command not found命令/ bin/sh失败,退出代码127

    尝试构建任何(新的或旧的)项目时出现此错误.我的节点是版本4.2.1,react-native是版本0.1.7.我看过其他有相同问题的人,所以我已经更新了本机的最新版本,但是我仍然无法通过xcode构建任何项目.解决方法要解决此问题,请使用以下步骤:>使用节点版本v4.2.1>cd进入[你的应用]/node_modules/react-native/packager>$sh./packager.s

  8. 反应原生 – 如何通过Xcode构建React Native iOS应用程序到设备?

    我试图将AwesomeProject应用程序构建到设备上.构建成功并启动屏幕显示,但后来我看到一个红色的“无法连接到开发服务器”屏幕.它表示“确保节点服务器正在运行–从Reactroot运行”npmstart“.看起来节点服务器已经运行,因为当我做npm启动时,我收到一个EADDRINUSE消息,表示该端口已经在使用.解决方法从设备访问开发服务器您可以使用开发服务器快速迭代设备.要做到这一点,你的

  9. 静音iOS推送通知与React Native应用程序在后台

    我有一个ReactNative应用程序,我试图获得一个发送到JavaScript处理程序的静默iOS推送通知.我看到的行为是AppDelegate中的didReceiveRemoteNotification函数被调用,但是我的JavaScript中的处理程序不会被调用,除非应用程序在前台,或者最近才被关闭.我很困惑的事情显然是应用程序正在被唤醒,并且它的didReceiveRemoteNotifi

  10. 如何为iOS的React Native设置分析

    所以我已经完成了一个针对iOS的ReactNative项目,但是我想在其中分析.我尝试了react-native-google-analytics软件包,但是问题阻止了它的正常工作.此外,react-native-cordova-plugin软件包只适用于Android,因此插入Cordova插件进行分析的能力现在已成为问题.我也没有Swift/ObjectiveC的经验,所以将完全失去GA的插入.有没有人有任何建议如何连接GoogleAnalytics的ReactNativeforiOS?

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部