Галоўная / Артыкулы / Выключэнне функцыі Latest-Value Ref: Адзінкты эфекта з useEffectEvent

Выключэнне функцыі Latest-Value Ref: Адзінкты эфекта з useEffectEvent

Дазвольце даклэ расказаць, як функция useEffectEvent у React 19.2 аддзеля нерэакtyўную логіку ад эфекта, усуняе нештатныя способы выкарыстоўвання ref з памятайчым значэннем, і калі гакі хук ўсё-такі не являецца правым інструментам.

917 слоў

У багацькох проектах на React є элемент useRef, якім прагнець толькі зберагчы “пасляпэўшуюе значэнне”, каб эфект не быў залежным ад яго. Штучка працуе, але спрыважвае намер. У React 19.2 элемент useEffectEvent стаў стабільным, чым даны патэрн атрымаў статус хука першага класу. У канцы вы зможазеце раздзеліць рэактыўную і нерэактыўную логіку ўнутры эфекта, а таксама зразумеце, калі хук не паслужыць.

Масэва залежнасцяў, якая не можа выграць

Конфлікт выступае ў разы давгачасных з’яўленняў: чат-камнатах, стрімах через WebSocket, сесіях аналітыкі, падпісах. Элемент ChatRoom прымеў roomId і theme. Эфект должен паўтарна наладзіць з’яўленне, калі зменяецца roomId, але чытае theme толькі для стылювання паведамлення пасля наладжэння.

Дадзіце theme у массив залежнасцей, і пераход на темную модулі без прычыны знішчае сокет. Якщо яго не дадзіць, правіла лінтингу скаржаюцца; якшо іх замовчыць, калебак падтрымвае theme, зафіксаваны ў старым рэндеры, што являе сабою класычны прыклад застарэлага клоузура.

function ChatRoom({ roomId, theme }) {
  useEffect(() => {
    const connection = createConnection(roomId);
    connection.on('connected', () => {
      showNotification('Connected!', theme); // stale after theme changes
    });
    connection.connect();
    return () => connection.disconnect();
  }, [roomId, theme]); // theme shouldn't trigger a reconnect
}

Рашэнне, якое выкарыстоўвалі большасць команд

Звычны спосаб быў копіюваць theme у ref пад час кожнага рэндеру і чытаць ref.current у калебаке. Цэе працуе, але шаг сінхронізацыі лёгка забыць, і ніч у кодзе не паказвае, што логіка свядома ўзначна нерэакtyўная.

Раздзеленыя логікі запуску падзеяў за дапамогою useEffectEvent

useEffectEvent выдзеляе нерэакtyўную частку эфекту ў самастоятнюю функцыю. Гэтая функцыя завжды бачыць найновейшыя пропсы і стан, ўвокаліванне яе ніколі не перзапускае эфект, і ёй не дадаёцца месца ў массиве залежнасцей.

Нижэйшыя часткі коду паказваюць, як утвараецца прыемніка паведамленняя onConnected. Ёго дзеянне заключаецца толькі у стварэнні з’ёднання, рэгістрацыі прыемніка, падключэнні і роз’ўязванні з’ёднання пад час чысткі.

import { useEffect, useEffectEvent } from 'react';

function ChatRoom({ roomId, theme }) {
  const onConnected = useEffectEvent(() => {
    showNotification('Connected!', theme); // always fresh, never a dependency
  });

  useEffect(() => {
    const connection = createConnection(roomId);
    connection.on('connected', () => onConnected());
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]); // only roomId matters now
}

Як залежнае значэнне застаёцца толькі roomId. theme не быў схованы ад інструменту лінтингу; ён быў перекласыфікаваны. Аплявленне паведамлення праз toast калі стварываецца з’ёднанне — это рэакція на падзею, таму гэта ўжо логіка падзей, а не сінхронізацыя. Змена тэмы больш не падключае занова, і наступнае паведамлення все равно відображаецца ў тэме, якая ў даны момент выкорыстоўваецца.

Канцэптуальна гэта тое, што робіў хак з ref уручну: React прымае функцыю, яку вы вызываеце з эфекту, так, каб яна вказвала на закрыцце пасляльнейшага рэндару, і тады намер стае зрозумелым.

Што вы отрымаеце

  • Больш няма застарэлых закрыццяў. Эфект-падзея чытае найновейшыя значэнні без неабходнасці ручных апдэйтаў ref.
  • Менша колькасць беспрагатных павторных запускаў. Эфект выконваецца толькі тады, калі змянюецца значэнне, з якім ён дзейсна сінхронізуецца.
  • Чыстэйшая ментальная модель. Эфекты означаюць «падтрымайце гэты систему сінхронной з гэтым станам»; падзеі эфекта означаюць «зрабіце гэта, калі тое адбудзецца».
  • У дакументах React з эры React 18 прасілі разработчыкаў аддзеляць падзеі ад эфектаў; версія 19.2 нарэшце падтрымае гэтыя рэкамендаціі за дапамогою стабільнага хука. Па іншым праблемам адзірніце нашый аптак у формате React 19.2: Activity, useEffectEvent і статычная рэндарызацыя.

    Перш чым яго вжываць

    useEffectEvent не заменяе useEffect, і ён не ўсунець узагальнёны спосаб для скорачэння массиваў залежнасцяў.

    • Ўжыце яго толькі тады, калі змена значэння не павінна перазапускать эфект. Якщо ж павінна, значэння трэба разместіць у масе.
    • Ніколі не вызывайце Effect Event пад час атрыбутавання; вызывайце яго з усередзіння эфектаў, такіх як калебачыцья падпісаў або таймеры.
    • Залічвайце яго локальна для сваёй складовай, а не перадавайце яго дзецям як проп.
    • Якщо большая частка эфекта знаходзіцца ўнутрь Effect Event, логіка, верагодна, павінна быць у звычным працоўніку адбудов.

    До версіі 19.2 гак існаваў толькі ў эксперыментальных версіях, таму проекты на 19.0 або 19.1 павінны апгрэйдавацца, каб выкарыстоўваць стабільны API.

    Ключовыя выводы

    • Кожны спосаб «зберагчы найновейшае значэння ў ref» яўляецца кандыдатам на викорыстоўванне useEffectEvent.
    • Для кожнай залежнасці запытайце ся, чы рэшта эфекта павінна перазапускаться; якщо няма, код, які яго чытае, ўжо є логікаю адбудовы.
  • API ў меры маленькі, але ён называе проблему, з якою сталкаваецца практычна кожны разработчык на React, не ведаючы, як ёй даты назву.
  • Спадні матэрыялы