React 新特性 Hooks 讲解及实例(三)

Stella981 等级 241 0 0

本文是 React 系列的第三篇

React 新特性讲解及实例(一)

React 新特性 Hooks 讲解及实例(二)

想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你!

使用 Context Hooks

React 新特性 Hooks 讲解及实例(三)

使用 Context ,首先顶层先声明 Provier 组件,并声明 value 属性,接着在后代组件中声明 Consumer 组件,这个 Consumer 子组件,只能是唯一的一个函数,函数参数即是 Context 的负载。如果有多个 Context ,ProviderConsumer 任意的顺序嵌套即可。

此外我们还可以针对任意一个 Context 使用 contextType 来简化对这个 Context 负载的获取。但在一个组件中,即使消费多个 Context,contextType 也只能指向其中一个

React 新特性 Hooks 讲解及实例(三)

在 Hooks 环境中,依旧可以使用 Consumer,但是 ContextType 作为类静态成员肯定是用不了。Hooks 提供了 useContext,不但解决了 Consumer 难用的问题同时也解决了 contextType 只能使用一个 context 的问题。

来个使用类形式的例子:

class Foo extends Component {
  render() {
    return (
      <CountContext.Consumer>
        {
          count => <h1>{count}</h1>
        }
      </CountContext.Consumer>
    )
  }
}

function App (props) {
  const [count, setCount] = useState(0);

  return (
    <div>
      <button type="button"
        onClick={() => {setCount(count + 1) }}
      >
        Click({count})
      </button>
      <CountContext.Provider value={count}>
        <Counter />
      </CountContext.Provider>
    </div>
  )
}

以上就不说解释了,第一篇已经讲过了,接着将 Foo 改成用 contextType 的形式:

class Foo extends Component {
  static contextType = CountContext;
  render() {
    const count = this.context
    return (
      <h1>{count}</h1>
    )
  }
}

接着使用 useContext 形式:

function Foo () {
  const count = useContext(CountContext)
  return (
    <h1>{count}</h1>
  )
}

useContext 接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 <CountContext.Provider> 的 value prop 决定。

当组件上层最近的 <CountContext.Provider> 更新时,该 Hook 会触发重渲染,并使用最新传递给 CountContext provider 的 context value 值。

别忘记 useContext 的参数必须是 context 对象本身:

  • 正确: useContext(MyContext)
  • 错误: useContext(MyContext.Consumer)
  • 错误: useContext(MyContext.Provider)

调用了 useContext 的组件总会在 context 值变化时重新渲染。如果重渲染组件的开销较大,你可以 通过使用 memoization 来优化。

使用 Memo Hooks

meno 用来优化函数组件重渲染的行为,当传入属性值都不变的情况下,就不会触发组件的重渲染,否则就会触发组件重渲染。

useMemo 与 memo

meno针对的是一个组件的渲染是否重复执行,而 useMemo 定义的是一个函数逻辑是否重复执行。

React 新特性 Hooks 讲解及实例(三)

来个粟子:

function Foo (props) {
  return (
    <h1>{props.count}</h1>
  )
}
function App (props) {
  const [count, setCount] = useState(0);

  const double = useMemo(() => {
    return count * 2
  }, [count])
  
  return (
    <div>
      <button type="button"
        onClick={() => {setCount(count + 1) }}
      >
        Click({count}) double: ({double})
      </button>
      <Foo count={count}/>
    </div>
  )
}

运行结果:

React 新特性 Hooks 讲解及实例(三)

如上所示,useMemo 语法与 useEffect 是一致的。第一个参数是需要执行的逻辑函数,第二个参数是这个逻辑依赖输入变量组成的数组,如果不传第二个参数,这 useMemo 的逻辑每次就会运行,useMemo 本身的意义就不存在了,所以需要传入参数。所以传入空数组就只会运行一次,策略与 useEffect 是一样的,但有一点比较大的差异就是调用时机,useEffect 执行的是副作用,所以一定是渲染之后才执行,但 useMemo 是需要返回值的,而返回值可以直接参与渲染,因此 useMemo 是在渲染期间完成的。

接下来改造一下 useMemo,让它依赖 count 如下:

const double = useMemo(() => {
  return count * 2
}, [count])

接着只有当 count 变化时,useMemo 才会执行。

React 新特性 Hooks 讲解及实例(三)

再次修改 useMemo, 如下:

const double = useMemo(() => {
  return count * 2
}, [count === 3])

现在能断定,count 在等于 3 之前,由于这个条件一直保存 false 不变,double 不会重新计算,所以一直是 0,当 count 等于 3,double 重新计算为 6,当 count 大于 3,double 在重新计算,变成 8,然后就一直保存 8 不变。

React 新特性 Hooks 讲解及实例(三)

记住,传入的 useMemo 的函数会在渲染期间执行,请不要在这个函数内部执行与渲染无关的听任,诸如副作用这类操作属于 useEffect 的适用范畴,而不是 useMemo

你可以把 useMemo 作为性能优化的手段,但不要把它当成语义上的保证。

使用 useCallback Hooks

接下先看一下使用 memo 优化子组件的例子。

const Foo = memo (function Foo (props) {
  console.log('Counter render')
  return (
    <h1>{props.count}</h1>
  )
})

function App (props) {
  const [count, setCount] = useState(0);

  const double = useMemo(() => {
    return count * 2
  }, [count === 3])

  return (
    <div style={{padding:'100px'}}>
      <button type="button"
        onClick={() => {setCount(count + 1) }}
      >
        Click({count}) double: ({double})
      </button>
      <Foo count={double}/>
    </div>
  )
}

使用 memo 包裹 Foo 组件,这样只有当 double 变化时,Foo 组件才会重新渲染,执行里面的 log,运行结果如下:

React 新特性 Hooks 讲解及实例(三)

现在在给 Foo 中的 h1 添加一个 click 事件:

const Foo = memo (function Foo (props) {
  console.log('Counter render')
  return (
    <h1 onClick={props.onClick}>{props.count}</h1>
  )
})

然后在 App 组件中声明 onClick 并传给 Foo 组件:

function App (props) {
  ...
  const onClick = () => {
    console.log('Click')
  }

  return (
    <div style={{padding:'100px'}}>
      ...
      <Foo count={double} onClick={onClick}/>
    </div>
  )
}

看下运行效果:

React 新特性 Hooks 讲解及实例(三)

可以看出,每次点击,不管 double 是否有变化, Foo 组件都会被渲染。那就说明每次 App 重新渲染之后, onClick 句柄的变化,导致 Foo 也被连带重新渲染了。count 经常变化可以理解,但是 onClick 就不应该经常变化了,毕竟只是一个函数而已,所以我们要想办法让 onClick 句柄不变化。

想想我们上面讲的 useMemo,可以这样来优化 onClick:

const onClick = useMemo(() => {
  return () => {
    console.log('Click')
  }
}, [])

由于我们传给 useMemo 的第二个参数是一个空数组,那么整个逻辑就只会运行一次,理论上我们返回的 onClick 就只有一个句柄。

运行效果:

React 新特性 Hooks 讲解及实例(三)

现在我们把 useCallback 来实现上页 useMemo 的逻辑。

const onClick = useCallback(() => {
  console.log('Click')
},[])

如果 useMemo 返回的是一个函数,那么可以直接使用 useCallback 来省略顶层的函数。

useCallback(fn, deps) 相当于 useMemo(() => fn, deps)

大家可能有一个疑问,useCallback 这几行代码明明每次组件渲染都会创建新的函数,它怎么就优化性能了呢。

注意,大家不要误会,使用 useCallback 确实不能阻止创建新的函数,但这个函数不一定会被返回,也就是说这个新创建的函数可能会被抛弃。useCallback解决的是解决的传入子组件的函数参数过多变化,导致子组件过多渲染的问题,这里需要理解好。

上述我们第二个参数传入的空数组,在实际业务并没有这么简单,至少也要更新一下状态。举个粟子:

function App (props) {
  ... 
  const [clickCount, setClickCount] = useState(0);
  const onClick = useCallback(() => {
    console.log('Click')
    setClickCount(clickCount + 1)
  },[clickCount, setClickCount])
  ...
}

在 APP 组件中在声明一个 useState,然后在 onClick 中调用 setClickCount,此时 onClick 依赖 clickCount,setClickCount

其实这里的 setClickCount 是不需要写的,因为 React 能保证 setState 每次返回的都是同个句柄。不信,可以看下官方文档 :

React 新特性 Hooks 讲解及实例(三)

这里的场景,除了直接使用 setClickCount + 1 赋值以外, 还有一种方式甚至连 clickCount都不用依赖。setState 除了传入对应的 state 最新值以外,还可以传入一个函数,函数的参数即这个 state 的当前值,返回就是要更新的值:

const onClick = useCallback(() => {
  console.log('Click')
  setClickCount((clickCount) => clickCount + 1)
},[])

小结

memo 根据属性来决定是否重新渲染组件一样,useMemo 可以根据指定的依赖不决定一段函数逻辑是否重新执行,从而优化性能。

如果 useMemo 的返回值是函数的话,那么就可以简写成 useCallback 的方式,只是简写而已,实际并没有区别。

需要特别注意的是,当依赖变化时,我们能断定 useMemo 一定重新执行。但是,即使依赖不变化我们不能假定它就一定不会重新执行,也就是说,它可以会执行,就是考虑内在优化结果。

我们可以把 useMemo, useCallback 当做一个锦上添花优化手段,不可以过度依赖它是否重新渲染,因为 React 目前没有打包票说一定执行或者一定不执行。

交流

干货系列文章汇总如下,觉得不错点个Star,欢迎 加群 互相学习。

https://github.com/qq44924588...

我是小智,公众号「大迁世界」作者,对前端技术保持学习爱好者。我会经常分享自己所学所看的干货,在进阶的路上,共勉!

关注公众号,后台回复福利,即可看到福利,你懂的。

React 新特性 Hooks 讲解及实例(三)

本文同步分享在 博客“前端小智”(SegmentFault)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

收藏
评论区

相关推荐

10分钟教你手写8个常用的自定义hooks
前言 Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。本文是一篇以实战为主的文章,主要讲解实际项目中如何使用hooks以及一些最佳实践,不会一步步再介绍一遍react hooks的由来和基本使用,因为写hooks的文章很多,而且官网对于react hooks的介绍也很详细
React事件绑定的几种方式
一、React事件是什么在react应用中,事件名都是用小驼峰格式进行书写,例如onclick要改写成onClick最简单的事件绑定如下:class ShowAlert extends React.Component   showAlert()      console.log("Hello World");      render()   
5分钟简单了解React
![](https://oscimg.oschina.net/oscnet/9f54c355-5b5d-4c33-bd28-d042a50ded80.jpg) 首先附上官网正文😀:\[React Hooks\](\[https://reactjs.org/docs/hooks-intro.html\](https://reactjs.org/docs/ho
10分钟教你手写8个常用的自定义hooks
https://juejin.im/post/5e57d0dfe51d4526ce6147f2 前言 -- Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。本文是一篇**以实战为主的文章,主要讲解实际项目中如何使用hooks以及一些最佳实践**,不会一步步再介
15 个很有用的自定义 React Hooks
React 提供了自定义 Hooks 的特性,我们可以根据这个特性创建出很多有意思的功能,今天就来分享一些个人认为还不错的 Hooks,共计 15 个。 如果对于 React Hooks 还不是特别熟悉的话,可以先参阅这篇文章:[React Hooks 温故而知新](https://www.oschina.net/action/GoToLink?url=h
React Hook
用React Hook写一个简单的登录表单示例,两种方式: 第一种: import React, { useState } from "react"; import ReactDOM from "react-dom"; function LoginForm() { const [username, setUsern
React Hooks 从入门到上手
![clipboard.png](https://oscimg.oschina.net/oscnet/up-b75ae8e61fcec15ce541e3e1d28eb8c3.png "clipboard.png") > Hooks are a new addition in React 16.8. They let you use state and ot
React Hooks 系列之4 useReducer
本系列将讲述 React Hooks 的使用方法,从 useState 开始,将包含如下内容: * useState * useEffect * useContext * useReducer * useCallBack * useMemo * useRef * custom hooks 掌握 React Hooks ap
React Hooks究竟是什么呢?
**摘要:** React Hooks原理解析。 * 原文:[快速了解 React Hooks 原理](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fsegmentfault.com%2Fa%2F1190000019966124) * 译者:前端小智 我们大部分 React 类
React Portals
Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案。常见场景:对话框、悬浮卡以及提示框! 定义一个 模态框组件: import React, { useRef, useEffect, } from 'react'; import ReactDOM from 'react-do
React hooks能替代HOC和render props么?
最近开始学习React,记录一下心得。 React hooks是16.8.0推出的,其目的是为了替换class,HOC,render props。那么本文将讨论啥是hooks,HOC,render props,以及hooks究竟能不能替换掉HOC,render props。 Hooks替代HOC和render props。 ===============
React 新特性 Hooks 讲解及实例(三)
本文是 React 系列的第三篇 [React 新特性讲解及实例(一)](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fsegmentfault.com%2Fa%2F1190000019414243) [React 新特性 Hooks 讲解及实例(二)](https://www.osc
React 新特性 React Hooks 的使用
关注 前端技术专栏 ,回复“ 资源 ”免费领取全套视频教程 **正文** ### **什么是Hooks?** Hooks是React 16.8的新增特性。 它可以让你在不编写class的情况下使用state以及其他的React特性。 是一些可以让你在函数组件里“钩入” React state及生命周期等特性的函数。 Ho
React系列
React系列-Mixin、HOC、Render Props(上) React系列-轻松学会Hooks(中) React系列-自定义Hooks很简单(下) > 我们在第二篇文章中介绍了一些常用的hooks,接着我们继续来介绍剩下的hooks吧 useReducer ---------- > 作为useState 的替代方案。它接收一个形如`(sta
React语法 [0]
React练习笔记 ========= * * * > reprintemps 2020-07-11 * * * 语法分析: ----- 1. 标签属性需要遵循规则 : 毕竟是替你转译,所以并不是真的在写标签; * 涉及JSX语法的部分,浏览器无法识别,必须转译,所以`<script type='text/babel' />