2024-04-06 React 脱围机制Recap

Tips

  1. 如何控制和同步 React 之外的信息
  2. 移除不必要的 EffectEffect Dependencies
  3. 组件之间共享逻辑-自定义hook

Keywords

代码运行的原因(交互/同步), useEffectEvent, forwardRef + useImperativeHandle, ** flushSync**, ref callback, useSyncExternalStore

Center Paragraph

ref 引用

const xxxRef = useRef() xxxRef是一个普通的 js object, 其改变不会触发 re-render
let timeoutId = null 是不会存活在组件的下一次渲染中的, 可以将 timeoutId 保存在 ref 之中

ref 操作 DOM

获取指向节点的Ref

低级组件(input)需要暴露DOM, 或者需要使用React中不存在的API, focus scrollIntoView

访问另一个自定义组件的DOM

forwardRef + useImperativeHandle

const MyInput = forwardRef((props, ref) => {
  return <input {...props} ref={ref} />;
});
const MyInput = forwardRef((props, ref) => {
  const realInputRef = useRef(null);
  useImperativeHandle(ref, () => ({
    // 只暴露 focus,没有别的
    focus() {
      realInputRef.current.focus();
    },
  }));
  return <input {...props} ref={realInputRef} />;
});

React 什么时候添加Ref

需要在可以操作dom的时候再增加ref . flashSync

  function handleAdd() {
    const newTodo = { id: nextId++, text: text };
    flushSync(() => {
      setText('');
      setTodos([ ...todos, newTodo]);      
    });
    listRef.current.lastChild.scrollIntoView({
      behavior: 'smooth',
      block: 'nearest'
    });
  }

使用Effect同步

Effect 会在渲染之后执行一些逻辑,以便与React系统之外(浏览器 API、第三方小部件,以及网络)进行同步. 比如 与服务器进行连接, **发送日志 **,控制 **非React组件 **(useRef)

Effect和Event的区别

Effect 不是像 Event一样通过特定的用户事件引起的, 而是渲染本身引起的副作用

编写 Effect steps

  • 声明Effect
  • 添加依赖
  • 按需添加cleanup函数,防止内存泄漏

开发环境执行Effect两次

  • 订阅+退订
  • 获取数据+中止
  • 不要执行商品购买操作
  • 应用程序启动时执行

移除不必要的 Effect

useSyncExternalStore
比如, 如果想根据state计算另一个state就不应使用Effect

几种不要的Effect的场景

  • 根据props/state 更新state -> 渲染时更新
  • props 变化的时候重置state -> key
  • props 变化的时候更新-> state 结构
  • 事件函数共享逻辑 -> 公共函数
  • 链式计算 -> 事件处理函数中计算
  • 初始化应用 -> 添加顶层变量/移出来
  • 通知父组件state变化 -> 对应的事件处理函数中
  • 数据传递给父组件 -> 父组件传递数据给子组件
  • 订阅外部store -> useSyncExternalStore
  • 获取数据(这不是键入事件) -> 自定义hook useData 清理函数

Effect LifeCycle

React Effect 的生命周期存在两个阶段:开始同步信息(Effect 主体部分),以及停止同步信息(Effect 的清理函数)

选择 Effect 的依赖项

依赖项可能导致出现无限循环的问题,或者 Effect 过于频繁地重新进行同步

  • 检查 Effect 是否表示了独立的同步
  • Event 和 Effect 分离
  • 避免将渲染期间计算出来的对象和函数作为依赖项

事件从Effect中分开

当你想让Effect中的一段代码不是响应的,应该如何做
React Effect 中,代码逻辑依赖一些 响应值,然而不希望它随着响应值改变而同步运行。此时,我们可以将这段逻辑放在 useEffectEvent 这个试验性 API 中

useEffectEvent

  • 只在 Effect 内部调用,隶属于 Effect 逻辑一部分
  • 不要传递给其他组件
import { experimental_useEffectEvent as useEffectEvent } from 'react';

移除不必要的Effect Dependency

不必须的dependency导致 re-xxx
Effect的依赖应该和Effect中的代码相对应,当移除Effect的X依赖的时候,要证明X不是依赖(改变依赖需要改变代码);

依赖object

解决方案是将对象中的state解构出来

function ChatRoom({ roomId }) {
const [messages, setMessages] = useState([]);
useEffect(() => {
  const connection = createConnection();
  connection.connect();
  connection.on('message', (receivedMessage) => {
    // 🔴 改成函数形式,并移除 `message` 依赖
    setMessages([...messages, receivedMessage]);
    setMessages(msgs => [...msgs, receivedMessage]);

  });
  return () => connection.disconnect();
}, [roomId, messages]); // ✅ All dependencies declared
// ...

组件间逻辑复用useHooks

自定义Hook, usxXXX 共享状态逻辑 而不是状态本身,最好抽离的是Effect相关

Keep in mind

  • refs 和 Effect 是 react 的一种脱围机制
  • do not suppress the dependency linter
  • 让你的自定义hook专注于具体的高级用例, 以用途名称命名自定义 hook
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容