资讯专栏INFORMATION COLUMN

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

aboutU / 899人阅读

摘要:粟例说明一下获取子组件或者节点的句柄指向已挂载到上的文本输入元素本质上,就像是可以在其属性中保存一个可变值的盒子。粟例说明一下渲染周期之间的共享数据的存储上述使用声明两个副作用,第一个每隔一秒对加,因为只需执行一次,所以每二个参为空数组。

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

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

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

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

使用 Ref Hooks

类组件中使用 Ref 一般有:

String Ref

Callback Ref

CreateRef

上述在函数组件中没有办法使用它们,取而代之的是 useRef Hooks。

useRef 主要有两个使用场景:

获取子组件或者 DOM 节点的句柄

渲染周期之间的共享数据的存储

大家可能会想到 state 也可跨越渲染周期保存,但是 state 的赋值会触发重渲染,但是 ref 不会,从这点看 ref 更像是类属性中的普通成员。

粟例说明一下:获取子组件或者 DOM 节点的句柄

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` 指向已挂载到 DOM 上的文本输入元素
    inputEl.current.focus();
  };
  return (
    <>
      
      
    
  );
}

本质上,useRef 就像是可以在其 .current 属性中保存一个可变值的“盒子”。

粟例说明一下:渲染周期之间的共享数据的存储

function App (props) {
  const [count, setCount] = useState(0);
  let it
  useEffect(() => {
    it = setInterval(() => {
      setCount(count => count + 1)
    }, 1000)
  } , [])

  useEffect(() => {
    if (count >= 5) {
      clearInterval(it)
    }
  })

  return (
    

{count}

) }

上述使用 useEffect 声明两个副作用,第一个每隔一秒对 count 加 1,因为只需执行一次,所以每二个参为空数组。第二个 useEffect 判断 count 大于等于时,停止对 count 的操作。

运行结果:

显示当 count5 的时候并没有停止,这是为什么呢?

因为在 clearInterval, it 这个变量已经不是 setInterval 赋值时的那个了,每次 App 重渲染都会重置它。这时候就可以使用 useRef 来解决这个问题。

function App (props) {
  const [count, setCount] = useState(0);
  const it = useRef(null)
  
  useEffect(() => {
    it.current = setInterval(() => {
      setCount(count => count + 1)
    }, 1000)
  } , [])

  useEffect(() => {
    if (count >= 5) {
      clearInterval(it.current)
    }
  })

  return (
    ...
  )
}

使用 useRef 来创建一个 it, 当 setInterval 返回的结果赋值给 itcurrent 属性。

运行结果:

你应该熟悉 ref 这一种访问 DOM 的主要方式。如果你将 ref 对象以

形式传入组件,则无论该节点如何改变,React 都会将 ref 对象的 .current 属性设置为相应的 DOM 节点。

然而,useRef()ref 属性更有用。它可以很方便地保存任何可变值,其类似于在 class 中使用实例字段的方式。

这是因为它创建的是一个普通 Javascript 对象。而 useRef() 和自建一个 {current: ...} 对象的唯一区别是,useRef 会在每次渲染时返回同一个 ref 对象。

请记住,当 ref 对象内容发生变化时,useRef 并不会通知你。变更 .current 属性不会引发组件重新渲染。如果想要在 React 绑定或解绑 DOM 节点的 ref 时运行某些代码,则需要使用回调 ref 来实现。
自定义 Hook

前面三篇,我们讲到优化类组件的三大问题:

方便复用状态逻辑

副作用的关注点分离

函数组件无 this 问题

对于组件的复用状态没怎么说明,现在使用自定义 Hook 来说明一下。

首先我们把上面的例子用到 count 的逻辑的用自定义 Hook 封装起来:

function useCount(defaultCount) {
  const [count, setCount] = useState(defaultCount);
  const it = useRef()
      
  useEffect(() => {
    it.current = setInterval(() => {
      setCount(count => count + 1)
    }, 1000)
  } , [])

  useEffect(() => {
    if (count >= 5) {
      clearInterval(it.current)
    }
  })
  
  return [count, setCount]
}


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

  return (
    

{count}

) }

运行效果:

可以看出运行效果跟上面是一样的。

定义 Hook 是一个函数,其名称以 “use” 开头,函数内部可以调用其他的 Hook。我们在函数自定义写法上似乎和编写函数组件没有区别,确实自定义组件与函数组件的最大区别就是输入与输出的区别。

再来一个特别的 Hook 加深一下映像。在上述代码不变的条件下,我们在加一个自定义 Hook 内容如下:

function useCounter(count) {
  return (
    

{count}

) }

在 App 组件调用:

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

运行效果:

我们自定义 useCounter Hook返回的是一个 JSX,运行效果是一样的,所以 Hook 是可以返回 JSX 来参与渲染的,更说明 Hook 与函数组件的相似性。

使用 Hook 的法则 只在最顶层使用 Hook

不要在循环,条件或嵌套中调用 Hook,确保总是在你的 React 函数的最顶层调用他们。遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。这上 React 能够在多次的 useState 和 useEffect 调用之间保持 hook 状态的正确。

只在 React 函数中调用 Hook

不要在普通的 JavaScript 函数中调用 Hook, 你可以:

在 React 的函数组件中调用 Hook

在自定义 Hook 中调用其它 Hook

Hooks 常见问题

以下主要说明几个典型的问题,当然这在官网上都有说明。

生命周期方法要如何对应到 Hook?

constructor:函数组件不需要构造函数。你可以通过调用 useState 来初始化 state。如果计算的代价比较昂贵,你可以传一个函数给 useState

getDerivedStateFromProps:改为 在渲染时 安排一次更新

shouldComponentUpdate:详见官网.

render:这是函数组件体本身。

componentDidMount, componentDidUpdate, componentWillUnmount:useEffect Hook 可以表达所有这些(包括 不那么 常见 的场景)的组合。

componentDidCatch and getDerivedStateFromError:目前还没有这些方法的 Hook 等价写法,但很快会加上。

如何强制更新一个 Hooks 组件

如果前后两次的值相同,useState 和 useReducer Hook 都会放弃更新。原地修改 state 并调用 setState 不会引起重新渲染。

通常,你不应该在 React 中修改本地 state。然而,作为一条出路,你可以用一个增长的计数器来在 state 没变的时候依然强制一次重新渲染:

  const [ignored, forceUpdate] = useReducer(x => x + 1, 0);

  function handleClick() {
    forceUpdate();
  }

可能的话尽量避免这种模式。

交流

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

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

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

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

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

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

相关文章

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

    摘要:来个使用类形式的例子以上就不说解释了,第一篇已经讲过了,接着将改成用的形式接着使用形式接收一个对象的返回值并返回该的当前值。如果的返回值是函数的话,那么就可以简写成的方式,只是简写而已,实际并没有区别。 本文是 React 系列的第三篇 React 新特性讲解及实例(一) React 新特性 Hooks 讲解及实例(二) 想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着...

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

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

    zero 评论0 收藏0
  • React系列 --- 从Mixin到HOC再到HOOKS()

    摘要:返回元素的是将新的与原始元素的浅层合并后的结果。生命周期方法要如何对应到函数组件不需要构造函数。除此之外,可以认为的设计在某些方面更加高效避免了需要的额外开支,像是创建类实例和在构造函数中绑定事件处理器的成本。 React系列 React系列 --- 简单模拟语法(一)React系列 --- Jsx, 合成事件与Refs(二)React系列 --- virtualdom diff算法实...

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

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

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

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

    Betta 评论0 收藏0

发表评论

0条评论

aboutU

|高级讲师

TA的文章

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