From 1e265875e22810d50f9c9e08295291d076b0dc14 Mon Sep 17 00:00:00 2001 From: "gorbenko.pavel" Date: Tue, 1 Sep 2026 16:49:44 +0500 Subject: [PATCH] =?UTF-8?q?fix(ServiceRequest):=20=D0=BF=D0=B0=D0=B4=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BF=D1=80=D0=BE=D1=81=D0=BC=D0=BE=D1=82?= =?UTF-8?q?=D1=80=D0=B0=20=D0=B7=D0=B0=D1=8F=D0=B2=D0=BA=D0=B8=20=E2=80=94?= =?UTF-8?q?=20=D1=85=D1=83=D0=BA=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20=D1=80?= =?UTF-8?q?=D0=B0=D0=BD=D0=BD=D0=B5=D0=B3=D0=BE=20return?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit useMemo с attachmentUrls стоял ПОСЛЕ `if (loading) return` — на первом рендере (загрузка) он не вызывался, на втором вызывался, порядок хуков менялся, и React ронял компонент с #310 «rendered more hooks than during the previous render». Карточка заявки на услуги не открывалась вообще. Перенёс расчёт выше ранних выходов. Заодно включил `react-hooks/rules-of-hooks` (error) — плагин давно в зависимостях и в коде есть eslint-disable для exhaustive-deps, то есть правила подразумевались, но в .eslintrc их забыли объявить. По остальному коду правило даёт 0 нарушений, так что этот баг был единственным. `exhaustive-deps` пока оставил выключенным: на текущем коде это 16 предупреждений, их разбор — отдельная задача, а не довесок к фиксу. Co-Authored-By: Claude Opus 5 --- .eslintrc.js | 17 +++++++++++++++ .../ServiceRequest/ServiceRequestView.tsx | 21 +++++++++++-------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index cb42e16..d8b5e1d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -11,7 +11,24 @@ module.exports = { 'ecmaVersion': 2018, 'sourceType': 'module' }, + plugins: ['react-hooks'], rules: { + // RATIONALE: Хук, вызванный не на каждом рендере (например, useMemo + // ПОСЛЕ раннего `return` для loading/error), ломает порядок + // хуков и валит компонент в рантайме с React #310 «rendered + // more hooks than during the previous render» — ошибка, + // которую видно только при открытии конкретного экрана. + // Правило ловит это на сборке. eslint-plugin-react-hooks + // давно в зависимостях, а в коде есть eslint-disable для + // exhaustive-deps — то есть правила подразумевались, но + // включить их в конфиг забыли (поймали на ServiceRequestView). + 'react-hooks/rules-of-hooks': 2, + // Зависимости эффектов — ВЫКЛЮЧЕНО намеренно. Правило полезное, но на + // текущем коде даёт 16 предупреждений (в основном намеренно опущенные + // onCodeLoaded/submit во всех формах). Шум такого объёма в каждой сборке + // приучает не читать варнинги вовсе — включать имеет смысл вместе с + // разбором этих мест, отдельной задачей, а не заодно с фиксом падения. + 'react-hooks/exhaustive-deps': 0, // Prevent usage of the JavaScript null value, while allowing code to access existing APIs that may require null. https://www.npmjs.com/package/@rushstack/eslint-plugin '@rushstack/no-new-null': 1, // Require Jest module mocking APIs to be called before any other statements in their code block. https://www.npmjs.com/package/@rushstack/eslint-plugin diff --git a/src/webparts/requestPortal/forms/ServiceRequest/ServiceRequestView.tsx b/src/webparts/requestPortal/forms/ServiceRequest/ServiceRequestView.tsx index 6e09061..70f1ace 100644 --- a/src/webparts/requestPortal/forms/ServiceRequest/ServiceRequestView.tsx +++ b/src/webparts/requestPortal/forms/ServiceRequest/ServiceRequestView.tsx @@ -107,6 +107,18 @@ const ServiceRequestView: React.FC = ({ context, itemI }; }, [itemId, listId]); + // Ссылки на скачивание для файлов строк «Приложения» (см. AnnexesSection). + // Считаем ДО ранних return'ов ниже: хук, вызванный только на части рендеров, + // ломает порядок хуков (React #310 — «rendered more hooks than during the + // previous render»), потому что на первом рендере (loading) его бы не было. + const attachmentUrls = useMemo(() => { + const map: Record = {}; + attachments.forEach((a) => { + map[a.fileName] = a.url; + }); + return map; + }, [attachments]); + if (loading) { return
Загрузка заявки…
; } @@ -118,15 +130,6 @@ const ServiceRequestView: React.FC = ({ context, itemI ); } - // Ссылки на скачивание для файлов строк «Приложения» (см. AnnexesSection). - const attachmentUrls = useMemo(() => { - const map: Record = {}; - attachments.forEach((a) => { - map[a.fileName] = a.url; - }); - return map; - }, [attachments]); - const sectionProps = { data, set, context, printOrgIndex, attachmentUrls }; const notify = (): void => {