资讯专栏INFORMATION COLUMN

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

_Dreams / 2691人阅读

摘要:来个使用类形式的例子以上就不说解释了,第一篇已经讲过了,接着将改成用的形式接着使用形式接收一个对象的返回值并返回该的当前值。如果的返回值是函数的话,那么就可以简写成的方式,只是简写而已,实际并没有区别。

本文是 React 系列的第三篇

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

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

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

使用 Context Hooks

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

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

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

来个使用类形式的例子:

class Foo extends Component {
  render() {
    return (
      
        {
          count => 

{count}

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

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

class Foo extends Component {
  static contextType = CountContext;
  render() {
    const count = this.context
    return (
      

{count}

) } }

接着使用 useContext 形式:

function Foo () {
  const count = useContext(CountContext)
  return (
    

{count}

) }

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

当组件上层最近的 更新时,该 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 定义的是一个函数逻辑是否重复执行。

来个粟子:

function Foo (props) {
  return (
    

{props.count}

) } function App (props) { const [count, setCount] = useState(0); const double = useMemo(() => { return count * 2 }, [count]) return (
) }

运行结果:

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

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

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

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

再次修改 useMemo, 如下:

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

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

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

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

使用 useCallback Hooks

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

const Foo = memo (function Foo (props) {
  console.log("Counter render")
  return (
    

{props.count}

) }) function App (props) { const [count, setCount] = useState(0); const double = useMemo(() => { return count * 2 }, [count === 3]) return (
) }

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

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

const Foo = memo (function Foo (props) {
  console.log("Counter render")
  return (
    

{props.count}

) })

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

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

  return (
    
...
) }

看下运行效果:

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

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

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

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

运行效果:

现在我们把 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 每次返回的都是同个句柄。不信,可以看下官方文档 :

这里的场景,除了直接使用 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...

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

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/105030.html

相关文章

  • React 特性 Hooks 讲解实例(四)

    摘要:粟例说明一下获取子组件或者节点的句柄指向已挂载到上的文本输入元素本质上,就像是可以在其属性中保存一个可变值的盒子。粟例说明一下渲染周期之间的共享数据的存储上述使用声明两个副作用,第一个每隔一秒对加,因为只需执行一次,所以每二个参为空数组。 想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你! React 新特性讲解及实例(一) React 新特性 Hooks 讲解及实...

    aboutU 评论0 收藏0
  • React 特性 Hooks 讲解实例(二)

    摘要:还可以返回另一个回调函数,这个函数的执行时机很重要。对于第二个我们可以通过返回一个回调函数来注销事件的注册。回调函数在视图被销毁之前触发,销毁的原因有两种重新渲染和组件卸载。 本文是 React 系列的第二篇 React 新特性讲解及实例(一) 想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你! 什么是 Hooks Hook 是 React 16.8 的新增特性。它可...

    zero 评论0 收藏0
  • React 特性讲解实例(一)

    摘要:接收一个属性,这个组件会让后代组件统一提供这个变量值。因此对于同一个对象而言,一定是后代元素。解决方法就是把内联函数提取出来,如下讲了这么多,我们还没有讲到其实我们已经讲完了的工作原理了。 本节主要讲解以下几个新的特性: Context ContextType lazy Suspense 错误边界(Error boundaries) memo 想阅读更多优质文章请猛戳GitHub博...

    Betta 评论0 收藏0
  • React Hooks 解析(下):进阶

    摘要:第一次了解这项特性的时候,真的有一种豁然开朗,发现新大陆的感觉。在绝大多数情况下,是更好的选择。唯一例外的就是需要根据新的来进行操作的场景。会保证在页面渲染前执行,也就是说页面渲染出来的是最终的效果。上面条规则都是为了保证调用顺序的稳定性。 欢迎关注我的公众号睿Talk,获取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、...

    APICloud 评论0 收藏0
  • 上课程推荐:《React Hooks 案例详解(React 进阶必备)》

    摘要:课程制作和案例制作都经过精心编排。对于开发者意义重大,希望对有需要的开发者有所帮助。是从提案转为正式加入的新特性。并不需要用继承,而是推荐用嵌套。大型项目中模块化与功能解耦困难。从而更加易于复用和独立测试。但使用会减少这种几率。 showImg(https://segmentfault.com/img/bVbpNRZ?w=1920&h=1080); 讲师简介 曾任职中软军队事业部,参与...

    Lin_YT 评论0 收藏0

发表评论

0条评论

_Dreams

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<