在 React 哲学中,除了使用 react hook 内部定义的状态,其他内容都被叫做 React 之外的系统
如:浏览器 API 缓存、DOM 操作等
而在 React 中于 React 之外的系统交互,就是脱围机制
useRef
我们常使用 useRef 来获取 React 组件实例,如果组件内部使用了 immutable 来抛出数据或者函数,就可以通过 useRef.current 来调用
但是除了用于获取组件实例,useRef 还可以和 useState 一样用于存储状态
import { useRef } from 'react';
export default function Counter() {
let ref = useRef(0);
function handleClick() {
ref.current = ref.current + 1;
alert('你点击了 ' + ref.current + ' 次!');
}
return (
<button onClick={handleClick}>
点我!
</button>
);
}
与 state 一样,ref 在重新渲染之间由 React 保留。但是,设置 state 会重新渲染组件,而更改 ref 不会!你可以通过 ref.current 属性访问该 ref 的当前值。
所以希望使用一个变量是不会被 react 重置的值时,可以考虑使用 useState 还是 useRef,useState 肯定不会错,即使当前这个状态不用于渲染渲染组件,但是以后可能会用 如果用 useRef,则以后迭代时需要改会用 useState
ref 就像组件的一个不被 React 追踪的秘密口袋。例如,可以使用 ref 来存储 timeout ID、DOM 元素 和其他不影响组件渲染输出的对象。
👆 解释了为什么 useEffect 会被执行两次,因为良好的 useEffect 如果使用了类似定时器的全局工具,会编写好 cleanup 方法,即对应在 useEffect 内 return 一个函数
因此 react 认为在开发环境下,如果有 cleanup 的存在,多执行一次也不会出现逻辑问题,而如果出现逻辑问题则证明需要补充 cleanup,所以
React 第一次执行 useEffect,会执行内部逻辑,并执行 cleanup React 第一次执行 useEffect,只会执行内部逻辑,不执行 cleanup
以此来让 useEffect 不编写 cleanup 导致隐性的问题可以提前暴露出来
🤔 但其实这是一种语法规范,使用 Linter 就可以提示了,而不需要写到框架的内部吧?是因为希望比 Linter 更强约束?
import { useState } from 'react';
export default function Counter() {
const [count] = useState({value: 1});
function handleClick() {
console.log('count',JSON.stringify(count))
count.value = count.value+1
console.log('new count',JSON.stringify(count))
}
return (
<button onClick={handleClick}>
点击
</button>
);
}
👆 只要不使用 setFn ,引用类型的数据是实时更新的,可以直接 console 出最新的值,因为没有触发重新渲染所以 useRef 的状态始终在当前的快照里
👇 基础数据类型同理,因为没有触发重新渲染,所有状态都会是当前快照
import { useState } from 'react';
export default function Counter() {
let [count] = useState(1); // ✨ 需要设置为 let
function handleClick() {
console.log('count',count)
count = count+1
console.log('new count',count)
}
return (
<button onClick={handleClick}>
你点击了
</button>
);
}
👆 也正是因为这样修改状态,而不经过 setFn 将会导致 react 不知道当前的状态是如何变化而来,因此最终的渲染可能不可预测
只要你改变的对象不用于渲染,React 就不会关心你对 ref 或其内容做了什么。