useRef
useRef不会触发新的渲染
与 state 一样,React 会在每次重新渲染之间保留 ref。但是,设置 state 会重新渲染组件,更改 ref 不会!
如图,我已经点击到了第六次,ref.current此时是6了,但是按钮上的数字还是0,这意味着没有重新渲染页面
ref 就像组件的一个不被 React 追踪的秘密口袋。例如,可以使用 ref 来存储 timeout ID、DOM 元素 和其他不影响组件渲染输出的对象。
当一条信息用于渲染时,将它保存在 state 中。当一条信息仅被事件处理器需要,并且更改它不需要重新渲染时,使用 ref 可能会更高效。
清除计时器的id
用ref引用来管理计时器的id,而不能用局部变量,是因为每当你的组件重新渲染时(例如当你设置 state 时),所有局部变量都会从头开始初始化。这就是为什么你不能将 timeout ID 保存在像 timeoutID 这样的局部变量中,然后期望未来另一个事件处理器“看到”它。相反,将它存储在一个 ref 中,React 将在渲染之间保留它。
useRef 创建的引用对象在组件的整个生命周期中保持不变,即使组件重新渲染。这样,我们就可以在组件的任何地方访问和操作这个计时器ID,而不用担心它会被重新初始化。
互相干扰的timeId
错误写法里面,每次防抖,清除计时器id时,清除的是共享的局部变量,而不是引用,所以即使点的是另一个组件,也因为他们的timeId的共享性,导致 后来的点击把另一个组件的计时器id给清了,导致各个组件的alert是互相干扰的;
正确写法里,每个debounceButton的timeid是使用的引用ref,保证了他们的独立性。
文本的最新版本
使用 ref 操作 DOM
通常在react里是不需要操作dom的,但是,有时你可能需要访问由 React 管理的 DOM 元素 —— 例如,让一个节点获得焦点、滚动到它或测量它的尺寸和位置。在 React 中没有内置的方法来做这些事情,所以你需要一个指向 DOM 节点的 ref 来实现。
示例
1.创建Ref
2.把ref引用绑定到dom上,此时的InputRef.current就是代指的input框,或者说这个引用指向实际的DOM元素。
3.调用方法,操作dom
使用UseEffect同步
编写 Effect 需要遵循以下三个规则:
- 声明 Effect。默认情况下,Effect 会在每次渲染后都会执行。
- 指定 Effect 依赖。大多数 Effect 应该按需执行,而不是在每次渲染后都执行。例如,淡入动画应该只在组件出现时触发。连接和断开服务器的操作只应在组件出现和消失时,或者切换聊天室时执行。文章将介绍如何通过指定依赖来控制如何按需执行。
- 必要时添加清理(cleanup)函数。有时 Effect 需要指定如何停止、撤销,或者清除它的效果。例如,“连接”操作需要“断连”,“订阅”需要“退订”,“获取”既需要“取消”也需要“忽略”。你将学习如何使用 清理函数 来做到这一切。
让Video组件与外部系统保持一致
基本思路,用一个ref去引用video的dom元素,父组件维护isPlaying这个值,然后子组件需要与父组件的isPlaying状态同步,由此调用dom的play或者pause dom方法
一个错误的做法
这段代码之所以不正确,是因为它试图在渲染期间对 DOM 节点进行操作。在 React 中,JSX 的渲染必须是纯粹操作,不应该包含任何像修改 DOM 的副作用。
也就是说,我们不应该在组件渲染的过程中操作dom,上面这个写法,是在渲染组件的过程中试图对dom节点进行操作,此时还是渲染阶段,dom节点甚至没有生成,则pause,play方法判空了。
解决办法:使用useEffect包裹这个副作用
我个人的理解是,初次渲染的时候会跳过effect代码,等渲染完成之后再执行这里的副作用。避免了初次ref未初始化的问题。另外把副作用包含着effect里,保证了组件的纯粹性。
个人总结:可以得知UseEffect的执行时机
这个例子里在useeffect里调用操作dom的方法,会保证当组件完成渲染之后才去调用操作dom的方法,而不会判空。
这里有一个有意思的证明,那就是在useEffect里操作document.tltle,你会发现页面里的数字增加和标题里的点击次数增加之间有一定延迟,这正好说明了use effect是在组件完成挂载之后才执行的。
使用Effect依赖
在effect的第二个参数上添加依赖项,是解决effect重复执行的方法
传空数组
意味着 useEffect 中的副作用函数只在组件首次渲染时执行一次,并且不依赖于任何状态或属性的变化。换句话说,这个副作用函数只在组件挂载和卸载时执行,也就是挂载组件之后,这个useEffect将永远不会执行了。
依赖数组里维护了一个isPlaying,这意味着如果 isPlaying 在上一次渲染时与当前相同,它应该跳过重新运行 Effect,这样一来只有初次挂载,以及isplaying与上一次渲染不同时,会调用这个effect里的逻辑。
使用Effect清理函数(cleanup)
清理函数的执行时机
每当重新执行 Effect 之前,React 都会调用清理函数;组件被卸载时,也会调用清理函数。
下面这个例子的依赖是toggle,也就是每次点击toggle取反,触发重新渲染,重新渲染之后触发effect,从控制台看出,在初次渲染,打印了effect runs,这符合空数组的特性,下一次点击,在重新执行effect之前,会调用上一次的清理函数,我们看见是先打印了两行清理函数的内容,才执行了下一次的effect
清理函数的示例
清除上一次effect的计时器
使用清理函数,减少页面加载到一半之后返回的请求
当没有清理函数时, 进入到页面,组件挂载就会重新渲染,并且alert提示。如果页面刚进去,还没加载完就退出了(组件清除了),这里的alert,重新渲染依然会发生。
加入了清理函数之后就解决了这个问题。这里给了一个isCancelled订阅