{"componentChunkName":"component---src-templates-docs-js","path":"/docs/hooks-rules.html","result":{"data":{"markdownRemark":{"html":"<p><em>Хуки</em> — нововведение в React 16.8, которое позволяет использовать состояние и другие возможности React без написания классов.</p>\n<p>Хуки — обычные JavaScript-функции, но существует два правила, которым нужно следовать. Чтобы автоматически их применять мы создали <a href=\"https://www.npmjs.com/package/eslint-plugin-react-hooks\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">плагин для линтера</a>:</p>\n<h3 id=\"only-call-hooks-at-the-top-level\"><a href=\"#only-call-hooks-at-the-top-level\" aria-hidden class=\"anchor\"><svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Используйте хуки только на верхнем уровне </h3>\n<p><strong>Не используйте хуки внутри циклов, условных операторов или вложенных функций.</strong> Вместо этого всегда используйте хуки только на верхнем уровне React-функций. Исполнение этого правила гарантирует, что хуки вызываются в одинаковой последовательности при каждом рендере компонента. Это позволит React правильно сохранять состояние хуков между множественными вызовами <code class=\"gatsby-code-text\">useState</code> и <code class=\"gatsby-code-text\">useEffect</code>. (Если вам интересно, подробное объяснение <a href=\"#explanation\">ниже</a>.)</p>\n<h3 id=\"only-call-hooks-from-react-functions\"><a href=\"#only-call-hooks-from-react-functions\" aria-hidden class=\"anchor\"><svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Вызывайте хуки только из React-функций </h3>\n<p><strong>Не вызывайте хуки из обычных функций JavaScript.</strong> Вместо этого можно:</p>\n<ul>\n<li>✅ Вызывать хуки из функционального компонента React.</li>\n<li>✅ Вызывать хуки из пользовательского хука (мы научимся делать это <a href=\"/docs/hooks-custom.html\">на следующей странице</a>).</li>\n</ul>\n<p>Следуя этому правилу, можно гарантировать, что вся логика состояния компонента чётко видна из исходного кода.</p>\n<h2 id=\"eslint-plugin\"><a href=\"#eslint-plugin\" aria-hidden class=\"anchor\"><svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Плагин для ESLint </h2>\n<p>Мы выпустили плагин для ESLint <a href=\"https://www.npmjs.com/package/eslint-plugin-react-hooks\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"><code class=\"gatsby-code-text\">eslint-plugin-react-hooks</code></a>, который принуждает к соблюдению этих двух правил. Если хотите испытать его в деле, добавьте этот плагин в ваш проект.</p>\n<p>Этот плагин включен по умолчанию в <a href=\"/docs/create-a-new-react-app.html#create-react-app\">Create React App</a>.</p>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"gatsby-code-bash\"><code class=\"gatsby-code-bash\"><span class=\"token function\">npm</span> <span class=\"token function\">install</span> eslint-plugin-react-hooks --save-dev</code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"gatsby-code-jsx\"><code class=\"gatsby-code-jsx\"><span class=\"token comment\">// Конфигурация ESLint</span>\n<span class=\"token punctuation\">{</span>\n  <span class=\"token string\">\"plugins\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token comment\">// ...</span>\n    <span class=\"token string\">\"react-hooks\"</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token string\">\"rules\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// ...</span>\n    <span class=\"token string\">\"react-hooks/rules-of-hooks\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"error\"</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// Проверяем правила хуков</span>\n    <span class=\"token string\">\"react-hooks/exhaustive-deps\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"warn\"</span> <span class=\"token comment\">// Проверяем зависимости эффекта</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p><strong>Вы можете пропустить остаток этой страницы и перейти к созданию <a href=\"/docs/hooks-custom.html\">собственного хука</a>.</strong> Но если вам интересно, ниже приведено объяснение, почему правила хуков необходимы.</p>\n<h2 id=\"explanation\"><a href=\"#explanation\" aria-hidden class=\"anchor\"><svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Объяснение </h2>\n<p>Как мы <a href=\"/docs/hooks-state.html#tip-using-multiple-state-variables\">ранее узнали,</a> хуки состояния или эффектов в одном и том же компоненте можно использовать многократно:</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"gatsby-code-jsx\"><code class=\"gatsby-code-jsx\"><span class=\"token keyword\">function</span> <span class=\"token function\">Form</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 1. Используем переменную состояния name</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>name<span class=\"token punctuation\">,</span> setName<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Мэри'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 2. Используем эффект для сохранения данных формы</span>\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">function</span> <span class=\"token function\">persistForm</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    localStorage<span class=\"token punctuation\">.</span><span class=\"token function\">setItem</span><span class=\"token punctuation\">(</span><span class=\"token string\">'formData'</span><span class=\"token punctuation\">,</span> name<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 3. Используем переменную состояния surname</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>surname<span class=\"token punctuation\">,</span> setSurname<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Поппинс'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 4. Используем эффект для обновления заголовка страницы</span>\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">function</span> <span class=\"token function\">updateTitle</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    document<span class=\"token punctuation\">.</span>title <span class=\"token operator\">=</span> name <span class=\"token operator\">+</span> <span class=\"token string\">' '</span> <span class=\"token operator\">+</span> surname<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// ...</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>Итак, как же React сопоставляет переменные состояния с вызовами <code class=\"gatsby-code-text\">useState</code>? Ответ таков: <strong>React полагается на порядок вызова хуков</strong>. Наш пример работает, потому что порядок вызова хуков одинаков при каждом рендере.</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"gatsby-code-jsx\"><code class=\"gatsby-code-jsx\"><span class=\"token comment\">// ------------</span>\n<span class=\"token comment\">// Первый рендер</span>\n<span class=\"token comment\">// ------------</span>\n<span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Мэри'</span><span class=\"token punctuation\">)</span>           <span class=\"token comment\">// 1. Инициализируем переменную name значением 'Мэри'</span>\n<span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span>persistForm<span class=\"token punctuation\">)</span>     <span class=\"token comment\">// 2. Добавляем эффект для сохранения данных формы</span>\n<span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Поппинс'</span><span class=\"token punctuation\">)</span>        <span class=\"token comment\">// 3. Инициализируем переменную surname значением 'Поппинс'</span>\n<span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span>updateTitle<span class=\"token punctuation\">)</span>     <span class=\"token comment\">// 4. Добавляем эффект для обновления заголовка страницы</span>\n\n<span class=\"token comment\">// -------------</span>\n<span class=\"token comment\">// Второй рендер</span>\n<span class=\"token comment\">// -------------</span>\n<span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Мэри'</span><span class=\"token punctuation\">)</span>           <span class=\"token comment\">// 1. Читаем переменную состояния name (аргумент игнорируется)</span>\n<span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span>persistForm<span class=\"token punctuation\">)</span>     <span class=\"token comment\">// 2. Заменяем эффект сохранения данных формы</span>\n<span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Поппинс'</span><span class=\"token punctuation\">)</span>        <span class=\"token comment\">// 3. Читаем переменную состояния surname (аргумент игнорируется)</span>\n<span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span>updateTitle<span class=\"token punctuation\">)</span>     <span class=\"token comment\">// 4. Заменяем эффект обновления заголовка страницы</span>\n\n<span class=\"token comment\">// ...</span></code></pre></div>\n<p>До тех пор пока порядок вызова хуков одинаков в каждом рендере, React может сопоставить некое внутреннее состояние с каждым из них. Но что случится, если мы поместим вызов хука (например, эффект <code class=\"gatsby-code-text\">persistForm</code>) внутрь условного оператора?</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"gatsby-code-jsx\"><code class=\"gatsby-code-jsx\">  <span class=\"token comment\">// 🔴 Нарушаем первое правило, помещая хук в условие</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>name <span class=\"token operator\">!==</span> <span class=\"token string\">''</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">function</span> <span class=\"token function\">persistForm</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      localStorage<span class=\"token punctuation\">.</span><span class=\"token function\">setItem</span><span class=\"token punctuation\">(</span><span class=\"token string\">'formData'</span><span class=\"token punctuation\">,</span> name<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span></code></pre></div>\n<p>Условие <code class=\"gatsby-code-text\">name !== &#39;&#39;</code> равняется <code class=\"gatsby-code-text\">true</code> при первом рендере, поэтому хук выполняется. Тем не менее, при следующем рендере пользователь может обратить это условие в <code class=\"gatsby-code-text\">false</code>, очистив поля формы. Теперь во время рендера хук будет пропущен и порядок вызовов хуков изменится.</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"gatsby-code-jsx\"><code class=\"gatsby-code-jsx\"><span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Мэри'</span><span class=\"token punctuation\">)</span>           <span class=\"token comment\">// 1. Читаем переменную состояния name (аргумент игнорируется)</span>\n<span class=\"token comment\">// useEffect(persistForm)  // 🔴 Хук пропускается!</span>\n<span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Поппинс'</span><span class=\"token punctuation\">)</span>        <span class=\"token comment\">// 🔴 2 (но ранее был 3). Ошибка при чтении переменной состояния surname</span>\n<span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span>updateTitle<span class=\"token punctuation\">)</span>     <span class=\"token comment\">// 🔴 3 (но ранее был 4). Ошибка при замене эффекта</span></code></pre></div>\n<p>React не будет знать, что вернуть для второго вызова хука <code class=\"gatsby-code-text\">useState</code>. React ожидал, что второй вызов хука в этом компоненте соответствует эффекту <code class=\"gatsby-code-text\">persistForm</code>, так же как при предыдущем рендере, но это больше не так. Начиная с этого момента, вызов каждого хука, следующего за пропущенным, также будет сдвинут на один назад, что приведёт к ошибкам.</p>\n<p><strong>Вот почему хуки должны вызываться на верхнем уровне компонента.</strong> Если мы хотим запускать эффект по условию, то можем поместить это условие <em>внутрь</em> хука:</p>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"gatsby-code-jsx\"><code class=\"gatsby-code-jsx\">  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">function</span> <span class=\"token function\">persistForm</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 👍 Первое правило больше не нарушается</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>name <span class=\"token operator\">!==</span> <span class=\"token string\">''</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      localStorage<span class=\"token punctuation\">.</span><span class=\"token function\">setItem</span><span class=\"token punctuation\">(</span><span class=\"token string\">'formData'</span><span class=\"token punctuation\">,</span> name<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p><strong>Эта проблема не будет вас беспокоить, если вы включите в свой проект <a href=\"https://www.npmjs.com/package/eslint-plugin-react-hooks\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">наше правило линтера</a>.</strong> Но теперь вы знаете, <em>почему</em> хуки работают таким образом и какие проблемы это правило предотвращает.</p>\n<h2 id=\"next-steps\"><a href=\"#next-steps\" aria-hidden class=\"anchor\"><svg aria-hidden=\"true\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Следующие шаги </h2>\n<p>Наконец-то мы готовы изучить, как <a href=\"/docs/hooks-custom.html\">написать свой собственный хук</a>! Пользовательские хуки позволят вам включить собственные хуки React в ваши абстракции и повторно использовать общую логику состояния в различных компонентах. </p>","frontmatter":{"title":"Правила хуков","next":"hooks-custom.html","prev":"hooks-effect.html"},"fields":{"path":"content/docs/hooks-rules.md","slug":"docs/hooks-rules.html"}}},"pageContext":{"slug":"docs/hooks-rules.html"}},"staticQueryHashes":[]}