1. 首页

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

使用 Ref Hooks

码农进阶题库,每天一道面试题 or Js小知识

类组件中使用 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 (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

本质上,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 ( <div style={{padding:'100px'}}> <h1>{count}</h1> </div> ) }

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

运行结果:

JS中文网 - 前端进阶资源分享

显示当 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 属性。

运行结果:

JS中文网 - 前端进阶资源分享

你应该熟悉 ref 这一种访问 DOM 的主要方式。如果你将 ref 对象以 <div ref={myRef} /> 形式传入组件,则无论该节点如何改变,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 ( <div style={{padding: '100px'}}> <h1>{count}</h1> </div> ) }

运行效果:

JS中文网 - 前端进阶资源分享

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

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

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


function useCounter(count) { return ( <h1>{count}</h1> ) }

在 App 组件调用:


function App (props) { const [count, setCount] = useCount(0); const Counter = useCounter(count) return ( <div style={{padding: '100px'}}> {Counter} </div> ) }

运行效果:

JS中文网 - 前端进阶资源分享

我们自定义 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 等价写法,但很快会加上。

码农进阶题库,每天一道面试题 or Js小知识

如何强制更新一个 Hooks 组件

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

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


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

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

系列推荐

作者:前端小智
链接:https://segmentfault.com/a/1190000019423616

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

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

链接:https://www.javascriptc.com/3367.html

« GitHub 上有个沙雕开发者,做了款斗图工具后火了…
Js中文周刊第58期»
Flutter 中文教程资源

相关推荐

QR code