React组件学习之Hooks使用

目录
  • 一、前言
  • 二、React Hooks
    • 2.1 useState
    • 2.2 useEffect
    • 2.3 useMemo
    • 2.4 useCallback
    • 2.5 useContext
    • 2.6 useRef
  • 三、总结

一、前言

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内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • React Hooks useReducer 逃避deps组件渲染次数增加陷阱

    目录 前言 自定义 Hooks 简单实现 在组件中使用自定义 Hooks 提前阻止 dispatch 触发 优化后再测试 结论 题外 前言 在快乐使用 React Hooks 开发自定义 Hooks 过程中,使用了 useEffect,useReducer,useRef,useCallback 等官方提供的 Hooks,将一些通用逻辑抽离出来,提高代码复用性. 但在组合使用 useEffect,useReducer,React.memo 时,发生了组件在状态未发生变化时触发渲染,因为此动作发生在

  • react中(含hooks)同步获取state值的方式

    目录 react(含hooks)同步获取state值 环境 代码示例 异步写成同步的方法 react hooks常用方法 1.useState 2.useEffect 3.useContext上下文传值 4.useReducer 5.useMemo 6.useRef react(含hooks)同步获取state值 环境 "dependencies": {     "babel-plugin-transform-decorators-legacy": "^1

  • React hooks useState异步问题及解决

    目录 React Hooks useState异步问题 原因 解决方法 React中useState异步更新小坑 问题点 React Hooks useState异步问题 最近在开发中遇到一个问题 我接口请求回来的数据 用useState存储起来. 但是我后面 去改变这个数据的时候每次拿到都是上次的数据没办法及时更新. 原因 useState 返回的更新状态方法是异步的,要在下次重绘才能获取新值.不要试图在更改状态之后立马获取状态. 解决方法 应该使用useRef 存储这个数据,在useEffe

  • React Hooks之usePolymerAction抽象代码结构设计理念

    目录 背景 设计理念 抽象代码结构 声明与定义 用法 高级用法 拆分/合并action API useSetState getAction polymerAction usePolymerActionState mergePolymerAction 背景 随着React Hooks 普及起来,组件逻辑写法变成函数式:逻辑函数声明.函数代码结构,随着逻辑增加复杂,代码组织混乱,函数声明散乱: 最关键的一点,每个人对代码理解不一样,有的喜欢在一个function 写大段逻辑,有的喜欢拆分逻辑很细,

  • React Hooks钩子中API的使用示例分析

    目录 hooks是什么 Hooks的作用 使用Hooks组件前后开发模式的对比 Hooks使用策略 为什么要有Hooks useState useEffect使用 useEffect依赖项 使用情景 useMemo使用 useMemo缓存组件方式 useMemo和useEffect的区别 useCallback使用 useContext使用 useRef使用 为什么在函数组件中无法使用ref 如何在类组件中使用ref属性 自定义hooks hooks是什么 hooks理解字面意思就是钩子,是一些

  • React组件学习之Hooks使用

    目录 一.前言 二.React Hooks 2.1 useState 2.2 useEffect 2.3 useMemo 2.4 useCallback 2.5 useContext 2.6 useRef 三.总结 一.前言 react组件分为类(class)组件和函数(function)组件. class 组件是通过继承模版类(Component.PureComponent)的方式开发新组件的,继承是 class 本身的特性,它支持设置 state,会在 state 改变后重新渲染,可以重写一

  • React进阶学习之组件的解耦之道

    前言 众所周知,React中的组件非常的灵活可扩展,不过随着业务复杂度的增加和许多外部工具库的引入,组件往往也会显得浮肿,接下来我们就一起来看看常见的几种,遵循单一职责原则的,组件分割与解耦的方法,话不多说了,来一起看看详细的介绍: 一.分割 render 函数 当一个组件渲染的内容较多时,有一个快速并且通用的方法是创建sub-render函数来简化原来庞大的 render class Panel extends React.Component { renderHeading() { // ..

  • 使用hooks写React组件需要注意的5个地方

    Hook是React16.8开始新增的特性.虽然React官方文档已经作出了针对React hooks的相关概念的讲解,但是光看官方文档是很难将hooks使用好的,在编写hooks的过程中很容易跳进陷阱和错误.本文总结了5个不好的地方. 01.不需要render的场景下使用useState 在函数组件中我们可以使用useState来管理状态,这使得对状态的管理变得很简单,但是也容易被滥用,我们通过下面的代码样例看下容易忽略的地方. 不推荐× function ClickButton(props)

  • React组件化学习入门教程讲解

    目录 模块化 模块 模块化 组件化 组件 组件化 函数式组件 创建函数组件 Props参数传递(重点) 复合函数组件 类式组件 创建实例 用户自定义组件 模块化 模块 理解:向外提供特定功能的js程序,一般就是一个js文件. 为什么:要拆成模块,随着业务逻辑增加,代码越来越多且复杂. 作用:复用js,简化js的编写,提高js运行效率. 模块化 当应用的js都以模块来编写,这个应用就是一个模块化的应用 组件化 组件 理解:用来实现局部功能效果的代码和资源的集合(html/css/js/img等等)

  • react组件的创建与更新实现流程详解

    目录 React源码执行流程图 legacyRenderSubtreeIntoContainer legacyCreateRootFromDOMContainer createLegacyRoot ReactDOMBlockingRoot createRootImpl createContainer createFiberRoot createHostRootFiber createFiber updateContainer 总结 这一章节就来讲讲ReactDOM.render()方法的内部实现

  • ES6下React组件的写法示例代码

    本文主要跟大家分享了ES6下React组件的写法示例,下面来一起看看详细的介绍: 一:定义React组件 class Hello extends React.Component { render() { return <h1>Hello, {this.props.value}</h1>; } } 二:声明prop类型与默认prop class Hello extends React.Component { // ... } Hello.propTypes = { value: Re

  • vue2.X组件学习心得(新手必看篇)

    VUEJS学习网址:https://cn.vuejs.org/ 此文章是用来记录自己的学习和实践心得. 关注点:父子组件之间的通信 看图说话: Pass Props 子组件本身与父组件是孤立的,通过子组件中显示声明的props属性,接收父组件数据; 父组件的数据更新时,子组件的prop会跟着更新: 此数据流动是单向的(看着); Emit Events 子组件使用$.emit(fn)向外抛出自己的内部触发的事件; 父组件是否监听?如果父组件需要监听,使用v-on绑定监听,触发对应事件; 以上为通用

  • 使用store来优化React组件的方法

    在使用 React 编写组件的时候,我们常常会碰到两个不同的组件之间需要共享状态情况,而通常的做法就是提升状态到父组件.但是这样做会有一个问题,就是尽管只有两个组件需要这个状态,但是因为把状态提到了父组件,那么在状态变化的时候,父组件以及其下面的所有子组件都会重新 render,如果你的父组件比较复杂,包含了其他很多子组件的话,就有可能引起性能问题. Redux 通过把状态放在全局的 store 里,然后组件去订阅各自需要的状态,当状态发生变化的时候,只有那些订阅的状态发生变化的组件才重新 r

  • React组件的三种写法总结

    React 专注于 view 层,组件化则是 React 的基础,也是其核心理念之一,一个完整的应用将由一个个独立的组件拼装而成. 截至目前 React 已经更新到 v15.4.2,由于 ES6 的普及和不同业务场景的影响,我们会发现目前主要有三种创建 React 组件的写法:1. ES5写法React.createClass,2. ES6写法React.Component,3. 无状态的函数式写法(纯组件-SFC). 你们最钟爱哪种写法呢?萝卜青菜各有所爱~ 每个团队都有自己的代码规范和开发模

  • React组件之间的通信的实例代码

    最近学习浅尝则止的学习了一下react.js这个UI的框架,react这个库给我的最大的感觉就是它能够完全的接管UI层,在要改变视图的东西的时候只需要改变其this.state中的状态.只要操作数据层的东西视图层就会发生变化,这一点我还是很喜欢的.可以摆脱对DOM的直接操作,毕竟直接来会比较复杂,本来应该是逻辑层js中混杂着各种css的字符串,对于我来说有点不爽(JSX中也混杂这标签,但我觉的不应该把它看作标签,看作语句会习惯一点). 回到几天的重点,讲react组件之间的状态传递. 上代码:

随机推荐