{"id":314672,"date":"2020-12-11T03:00:14","date_gmt":"2020-12-11T03:00:14","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=314672"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=314672","title":{"rendered":"\u042d\u043f\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0441\u0430\u0433\u0430 \u043f\u0440\u043e \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u0438\u0439 custom hook \u0434\u043b\u044f React (\u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440\u044b, sagas, rxjs)"},"content":{"rendered":"\n<div class=\"post__text post__text_v2\" id=\"post-content-body\">\n<h2>\u041f\u0440\u0435\u043b\u044e\u0434\u0438\u044f<\/h2>\n<p>\u0421\u0442\u043e\u044f\u043b\u0430 \u0437\u0430\u0434\u0430\u0447\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u0435\u043b\u043e\u0430\u0434\u0435\u0440 \u0441 \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u043c \u043e\u0442\u0441\u0447\u0435\u0442\u043e\u043c \u043d\u0430 \u0440\u0435\u0430\u043a\u0442\u0435. \u0422.\u043a. \u0433\u0443\u0433\u043b\u0438\u0442\u044c \u043d\u0435 \u0443\u043c\u0435\u044e \u0438 \u043e\u0447\u0435\u043d\u044c \u043b\u044e\u0431\u043b\u044e \u043b\u0435\u043f\u0438\u0442\u044c \u0432\u0435\u043b\u043e\u0441\u0438\u043f\u0435\u0434\u044b (\u0445\u043e\u0440\u043e\u0448\u0438\u0439 \u0434\u0432\u0443\u0445\u043c\u0435\u0441\u0442\u043d\u044b\u0439 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0441\u044f \u0438\u0437 \u0434\u0432\u0443\u0445 \u0432\u0435\u043b\u043e\u0441\u0438\u043f\u0435\u0434\u043e\u0432 &#171;\u0423\u043a\u0440\u0430\u0438\u043d\u0430&#187;), \u0442\u043e \u044f \u0431\u044b\u043b \u043e\u0431\u0440\u0435\u0447\u0435\u043d \u0433\u043b\u0443\u0431\u043e\u043a\u043e \u043a\u043e\u043f\u0430\u0442\u044c \u043d\u0430 \u043f\u043e\u043b\u044f\u0445 \u0430\u0441\u0438\u043d\u0445\u0440\u0435\u043d\u044c\u0449\u0438\u043d\u044b. \u0417\u0430\u0431\u0435\u0433\u0443 \u043d\u0430\u043f\u0435\u0440\u0451\u0434 \u0438 \u0441\u043a\u0430\u0436\u0443, \u0447\u0442\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b \u044d\u0442\u043e\u0442 \u043f\u0440\u0435\u043b\u043e\u0430\u0434\u0435\u0440 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440\u043e\u0432, redux-saga, rxjs. \u041e\u0447\u0435\u043d\u044c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 \u043e\u043f\u044b\u0442 \u0438 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f, \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u044f, \u0447\u0442\u043e \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0442\u0435\u043b\u044c\u0441\u0442\u0432, \u043a\u0440\u043e\u043c\u0435 \u0441\u0442\u0430\u0442\u0435\u0439, \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0449\u0438\u0445 \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u0435 \u0432\u0435\u0449\u0438, \u0438 \u043e\u0431\u0440\u044b\u0432\u043a\u043e\u0432 \u0441\u0443\u0433\u0443\u0431\u043e \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043d\u0430 stackoverflow, \u043d\u0435 \u043d\u0430\u0445\u043e\u0434\u0438\u043b.<\/p>\n<p>\u0418\u0442\u0430\u043a: \u0447\u0430\u0441\u0442\u044c \u043f\u0435\u0440\u0432\u0430\u044f &#8212; \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u043e\u0433\u043e \u0445\u0443\u043a\u0430.<\/p>\n<h2>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u0435\u043b\u043e\u0430\u0434\u0435\u0440\u0430<\/h2>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0448\u043a\u0430\u043b\u044b \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438, \u044f \u0432\u044b\u0431\u0440\u0430\u043b \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a \u043d\u0430 \u0441\u0430\u0439\u0442\u0435 (\u043c\u043e\u0436\u043d\u043e \u0432\u044b\u0431\u0438\u0440\u0430\u0442\u044c \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u044b), \u0442.\u043a. \u0432 \u043c\u043e\u0451\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044d\u0442\u043e \u0431\u044b\u043b\u0438 \u0441\u0430\u043c\u044b\u0435 \u0442\u044f\u0436\u0451\u043b\u044b\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u044b \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b.<\/p>\n<p>\u0422.\u043a. \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0432 \u0440\u0435\u0430\u043a\u0442\u0435, \u0442\u043e \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435 \u043e\u0444\u043e\u0440\u043c\u0438\u043b \u044d\u0442\u043e \u0432 \u0432\u0438\u0434\u0435 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u043e\u0433\u043e \u0445\u0443\u043a\u0430 \u0438 \u043d\u0430\u0437\u0432\u0430\u043b \u0435\u0433\u043e usePreloader.<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434 \u0441\u0430\u043c\u043e\u0433\u043e \u043f\u0440\u0435\u043b\u043e\u0430\u0434\u0435\u0440\u0430<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">import React from \"react\";  export default function Preloader() {   return (     &lt;div className=\"preloader__wrapper\"&gt;       &lt;div className=\"preloader\"&gt;         &lt;svg           width=\"300\"           height=\"300\"           viewBox=\"0 0 300 300\"           fill=\"none\"           xmlns=\"http:\/\/www.w3.org\/2000\/svg\"         &gt;           &lt;path             d=\"M300 150C300 232.843 232.843 300 150 300C67.1573 300 0 232.843 0 150C0 67.1573 67.1573 0 150 0C232.843 0 300 67.1573 300 150ZM20.7679 150C20.7679 221.373 78.6271 279.232 150 279.232C221.373 279.232 279.232 221.373 279.232 150C279.232 78.6271 221.373 20.7679 150 20.7679C78.6271 20.7679 20.7679 78.6271 20.7679 150Z\"             fill=\"#F3F3F3\"           \/&gt;           &lt;path             d=\"M289.616 150C295.351 150 300.037 154.655 299.641 160.376C297.837 186.392 289.275 211.553 274.72 233.336C258.238 258.003 234.811 277.229 207.403 288.582C179.994 299.935 149.834 302.906 120.736 297.118C91.6393 291.33 64.9119 277.044 43.934 256.066C22.9561 235.088 8.66999 208.361 2.88221 179.264C-2.90558 150.166 0.0649254 120.006 11.4181 92.5975C22.7712 65.1886 41.9971 41.7618 66.6645 25.2796C88.4468 10.725 113.608 2.16293 139.624 0.359232C145.345 -0.0374269 150 4.64906 150 10.384C150 16.1189 145.343 20.7246 139.627 21.1849C117.723 22.9486 96.5686 30.2756 78.2025 42.5475C56.9504 56.7477 40.3864 76.931 30.6052 100.545C20.8239 124.159 18.2647 150.143 23.2511 175.212C28.2375 200.28 40.5457 223.307 58.6191 241.381C76.6926 259.454 99.7195 271.762 124.788 276.749C149.857 281.735 175.841 279.176 199.455 269.395C223.069 259.614 243.252 243.05 257.453 221.797C269.724 203.431 277.051 182.277 278.815 160.373C279.275 154.657 283.881 150 289.616 150Z\"             fill=\"#6CAE30\"           \/&gt;         &lt;\/svg&gt;       &lt;\/div&gt;       &lt;div className=\"preloader__counter\"&gt;0%&lt;\/div&gt;     &lt;\/div&gt;   ); } <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0421\u0442\u0438\u043b\u0438 \u043f\u0440\u0435\u043b\u043e\u0430\u0434\u0435\u0440\u0430<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"css\">.App {   font-family: sans-serif;   text-align: center; }  .preloader__wrapper {   position: fixed;   display: flex;   flex-direction: column;   align-items: center;   justify-content: center;   top: 0;   left: 0;   width: 100%;   height: 100%;   background-color: #ffffff;   z-index: 2000; }  .preloader {   position: relative;   width: 18.75rem;   height: 18.75rem; } .preloader svg {   width: 18.75rem;   height: 18.75rem;   -webkit-animation: spin 4s infinite linear;   animation: spin 4s infinite linear; } .preloader__counter {   margin-top: 1.625rem;   padding-left: 1rem;   font-family: sans-serif;   font-size: 3.125rem;   line-height: 1;   color: #6cae30; }  @media (max-width: 900px) {   .preloader {     width: 6.25rem;     height: 6.25rem;   }   .preloader svg {     width: 6.25rem;     height: 6.25rem;   }   .preloader__counter {     margin-top: 0.75rem;     padding-left: 0.5rem;     font-size: 2.125rem;   } }  @-moz-keyframes spin {   from {     -moz-transform: rotate(0deg);   }   to {     -moz-transform: rotate(360deg);   } } @-webkit-keyframes spin {   from {     -webkit-transform: rotate(0deg);   }   to {     -webkit-transform: rotate(360deg);   } } @keyframes spin {   from {     transform: rotate(0deg);   }   to {     transform: rotate(360deg);   } } <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h2>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0445\u0443\u043a\u0430 <\/h2>\n<p>\u041b\u043e\u0433\u0438\u043a\u043e\u0439 \u0445\u0443\u043a\u0430 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u0438\u0441\u043a \u0432\u0441\u0435\u0445 \u0442\u044d\u0433\u043e\u0432 &lt;img&gt; \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u043d\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u044f load \u0438 error. \u0414\u0435\u043b\u0430\u0435\u0442\u0441\u044f \u044d\u0442\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u043a\u043e\u0434\u0430<\/p>\n<pre><code class=\"javascript\">const updateCounter = () =&gt; {   dispatch({     type: ACTIONS.SET_COUNTER,     data: state.counter + state.counterStep   }); };  const checkImageLoading = (url) =&gt; {   const imageChecker = new Image();   imageChecker.addEventListener(\"load\", updateCounter);   imageChecker.addEventListener(\"error\", updateCounter);   imageChecker.src = url; };<\/code><\/pre>\n<p>\u0423 \u0445\u0443\u043a\u0430 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 (state), \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0431\u0443\u0434\u0435\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u0448\u0430\u0433 \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u0438 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u0430. \u0412 \u0434\u0430\u043d\u043d\u043e\u0439 (\u043f\u0435\u0440\u0432\u043e\u0439) \u0447\u0430\u0441\u0442\u0438 \u043f\u0440\u0435\u043b\u043e\u0430\u0434\u0435\u0440 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0445\u0443\u043a\u043e\u0432 react, \u043d\u043e \u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0430\u0445. \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c redux \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u043d\u0435 \u0431\u0443\u0434\u0435\u043c, \u0442.\u043a. \u044d\u0442\u043e \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u0445\u0443\u043a \u0438 \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b \u0435\u0433\u043e \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c.<\/p>\n<details class=\"spoiler\">\n<summary>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 state.js<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">export const SET_COUNTER = \"SET_COUNTER\"; export const SET_COUNTER_STEP = \"SET_COUNTER_STEP\"; export const initialState = {   counter: 0,   counterStep: 0, }; export const reducer = (state, action) =&gt; {   switch (action.type) {     case SET_COUNTER:       return { ...state, counter: action.data };     case SET_COUNTER_STEP:       return { ...state, counterStep: action.data };     default:       throw new Error(\"This action is not applicable to this component.\");   } };  export const ACTIONS = {   SET_COUNTER,   SET_COUNTER_STEP, }; <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h2>\u0412\u0430\u0440\u0438\u0430\u043d\u0442 1 (\u043d\u0435 \u0440\u0430\u0431\u043e\u0447\u0438\u0439, \u0434\u043b\u044f \u043d\u0430\u0433\u043b\u044f\u0434\u043d\u043e\u0441\u0442\u0438). useReducer<\/h2>\n<p>\u042d\u0442\u043e\u0442 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u043f\u043e\u043a\u0430\u0437\u0430\u043d \u0434\u043b\u044f \u043d\u0430\u0433\u043b\u044f\u0434\u043d\u043e\u0441\u0442\u0438, \u0447\u0442\u043e\u0431\u044b \u0431\u044b\u043b\u043e \u043f\u043e\u043d\u044f\u0442\u043d\u043e, \u0432 \u043a\u0430\u043a\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u0431\u0435\u0437 useRef \u043d\u0435 \u043e\u0431\u043e\u0439\u0442\u0438\u0441\u044c. \u041c\u0435\u0442\u043e\u0434 <strong>checkImageLoading <\/strong>\u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0432 \u0446\u0438\u043a\u043b\u0435 \u043f\u0440\u0438 \u043c\u043e\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0438 \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 state \u0443 \u0432\u0441\u0435\u0445 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u0431\u0443\u0434\u0435\u0442 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u043d\u0430 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435.<\/p>\n<details class=\"spoiler\">\n<summary>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 usePreloader.js<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">import { useReducer, useEffect } from \"react\"; import { reducer, initialState, ACTIONS } from \".\/state\";  const PRELOADER_SELECTOR = \".preloader__wrapper\"; const PRELOADER_COUNTER_SELECTOR = \".preloader__counter\";  const usePreloader = () =&gt; {   const [state, dispatch] = useReducer(reducer, initialState);    const preloaderEl = document.querySelector(PRELOADER_SELECTOR);   const counterEl = document.querySelector(PRELOADER_COUNTER_SELECTOR);    const updateCounter = () =&gt; {     dispatch({       type: ACTIONS.SET_COUNTER,       data: state.counter + state.counterStep     });   };    const checkImageLoading = (url) =&gt; {     const imageChecker = new Image();     imageChecker.addEventListener(\"load\", updateCounter);     imageChecker.addEventListener(\"error\", updateCounter);     imageChecker.src = url;   };    useEffect(() =&gt; {     const imgArray = document.querySelectorAll(\"img\");     if (imgArray.length &gt; 0) {       dispatch({         type: ACTIONS.SET_COUNTER_STEP,         data: Math.floor(100 \/ imgArray.length) + 1       });       imgArray.forEach((img) =&gt; {         checkImageLoading(img.src);       });     }   }, []);    useEffect(() =&gt; {     if (counterEl) {       state.counter &lt; 100         ? (counterEl.innerHTML = `${state.counter}%`)         : hidePreloader(preloaderEl);     }   }, [state]);    return; };  const hidePreloader = (preloaderEl) =&gt; {   preloaderEl.remove(); };  export default usePreloader;<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h2>\u0412\u0430\u0440\u0438\u0430\u043d\u0442 2. useReducer + useRef<\/h2>\n<p>\u041e\u0431\u044a\u0435\u043a\u0442, \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0435\u043c\u044b\u0439 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e useRef \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u043d\u0430 \u043f\u0440\u043e\u0442\u044f\u0436\u0435\u043d\u0438\u0438 \u0432\u0441\u0435\u0433\u043e \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430. \u0412 \u043d\u0435\u0433\u043e \u044f \u0431\u0443\u0434\u0443 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0439 state \u0438 \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u0438\u0437 \u043a\u043e\u043b\u0431\u044d\u043a\u043e\u0432 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0445\u0443\u043a\u0430.<\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u044d\u0442\u043e\u0442 \u043e\u0431\u044a\u0435\u043a\u0442:<\/p>\n<pre><code class=\"javascript\">\/\/\u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044e \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 state const stateRef = useRef(state);<\/code><\/pre>\n<p>\u0412 \u043a\u043e\u043b\u0431\u044d\u043a\u0435 \u0441\u0441\u044b\u043b\u0430\u0442\u044c\u0441\u044f \u043d\u0430 state \u0447\u0435\u0440\u0435\u0437 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e current \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 stateRef:<\/p>\n<pre><code class=\"javascript\">const updateCounter = () =&gt; {   dispatch({     type: ACTIONS.SET_COUNTER,     data: stateRef.current.counter + stateRef.current.counterStep \/\/\u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u0435\u0440\u0443 \u043d\u0435 \u0438\u0437 state, \u0430 \u0438\u0437 stateRef   }); };<\/code><\/pre>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0441\u0441\u044b\u043b\u0430\u044e\u0441\u044c \u043d\u0430 state \u043f\u0440\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u0432 \u0440\u0430\u0437\u043c\u0435\u0442\u043a\u0435<\/p>\n<pre><code class=\"javascript\">stateRef.current.counter &lt; 100 \t? (counterEl.innerHTML = `${stateRef.current.counter}%`) \/\/\u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u0435\u0440\u0443 \u043d\u0435 \u0438\u0437 state, \u0430 \u0438\u0437 stateRef \t: hidePreloader(preloaderEl);<\/code><\/pre>\n<p>\u041f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 state, \u043d\u0443\u0436\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0443:<\/p>\n<pre><code class=\"javascript\">useEffect(() =&gt; {   stateRef.current = state; \/\/\u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 state, \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u044e \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0439 state   ... }, [state]);<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 useReducer + useRef<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">import { useReducer, useEffect, useRef } from \"react\"; import { reducer, initialState, ACTIONS } from \".\/state\";  const PRELOADER_SELECTOR = \".preloader__wrapper\"; const PRELOADER_COUNTER_SELECTOR = \".preloader__counter\";  const usePreloader = () =&gt; {   const [state, dispatch] = useReducer(reducer, initialState);    \/\/\u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044e \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 state   const stateRef = useRef(state);    const preloaderEl = document.querySelector(PRELOADER_SELECTOR);   const counterEl = document.querySelector(PRELOADER_COUNTER_SELECTOR);    const updateCounter = () =&gt; {     dispatch({       type: ACTIONS.SET_COUNTER,       data: stateRef.current.counter + stateRef.current.counterStep \/\/\u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u0435\u0440\u0443 \u043d\u0435 \u0438\u0437 state, \u0430 \u0438\u0437 stateRef     });   };    const checkImageLoading = (url) =&gt; {     const imageChecker = new Image();     imageChecker.addEventListener(\"load\", updateCounter);     imageChecker.addEventListener(\"error\", updateCounter);     imageChecker.src = url;   };    useEffect(() =&gt; {     const imgArray = document.querySelectorAll(\"img\");     if (imgArray.length &gt; 0) {       dispatch({         type: ACTIONS.SET_COUNTER_STEP,         data: Math.floor(100 \/ imgArray.length) + 1       });       imgArray.forEach((img) =&gt; {         checkImageLoading(img.src);       });     }   }, []);    useEffect(() =&gt; {     stateRef.current = state; \/\/\u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 state, \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u044e \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0439 state     if (counterEl) {       stateRef.current.counter &lt; 100         ? (counterEl.innerHTML = `${stateRef.current.counter}%`) \/\/\u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u0435\u0440\u0443 \u043d\u0435 \u0438\u0437 state, \u0430 \u0438\u0437 stateRef         : hidePreloader(preloaderEl);     }   }, [state]);    return; };  const hidePreloader = (preloaderEl) =&gt; {   preloaderEl.remove(); };  export default usePreloader; <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041d\u043e \u0442\u0443\u0442 \u043f\u043e\u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043d\u044c\u044e\u0430\u043d\u0441\u044b. \u0415\u0441\u043b\u0438 \u043f\u0440\u0438\u043d\u0443\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0442\u044c \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0430 (\u0437\u0430\u043a\u043b\u0430\u0434\u043a\u0430 network \u0432 devtools), \u0442\u043e \u0432\u0441\u0451 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e. \u0410 \u0435\u0441\u043b\u0438 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u043d\u043e\u0439, \u0442\u043e \u043f\u0440\u0435\u043b\u043e\u0430\u0434\u0435\u0440 \u0437\u0430\u0432\u0438\u0441\u0430\u0435\u0442 \u043d\u0430 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0438. \u0422.\u0435. \u0445\u0443\u043a<\/p>\n<pre><code>useEffect(() =&gt; {   ... }, [state]);<\/code><\/pre>\n<p>\u043d\u0435 \u043e\u0442\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 state. \u0410 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0441\u0441\u044b\u043b\u043a\u0430 stateRef.current \u043d\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0438 \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u043d\u0435\u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0439. \u042d\u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0440\u0435\u0430\u043a\u0442 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0438\u0440\u0443\u0435\u0442 \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u043a\u0443.<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u0440\u0435\u0448\u0438\u0442\u044c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443, \u0432\u043c\u0435\u0441\u0442\u043e useEffect \u043d\u0443\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c useLayoutEffect:<\/p>\n<pre><code class=\"javascript\">  useLayoutEffect(() =&gt; {     stateRef.current = state;     if (counterEl) {       stateRef.current.counter &lt; 100         ? (counterEl.innerHTML = `${stateRef.current.counter}%`)         : hidePreloader(preloaderEl);     }   }, [state]);<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043f\u0440\u0435\u043b\u043e\u0430\u0434\u0435\u0440 \u0441\u0442\u0430\u043b \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u043e. <\/p>\n<p><a href=\"https:\/\/ru.reactjs.org\/docs\/hooks-reference.html#useeffect\" rel=\"noopener noreferrer nofollow\">useEffect<\/a> &#8212; \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e. \u0422.\u0435. \u043f\u043e\u043a\u0430 \u043e\u043d \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0441\u044f &#8212; state \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u043c\u0435\u043d\u044f\u0442\u044c\u0441\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437.<\/p>\n<p><a href=\"https:\/\/ru.reactjs.org\/docs\/hooks-reference.html#uselayouteffect\" rel=\"noopener noreferrer nofollow\">useLayoutEffect<\/a> &#8212; \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e. \u0422.\u0435. \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u044d\u0442\u043e\u0442 \u0445\u0443\u043a \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d \u0438 \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u043a\u0430 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0430. <\/p>\n<p>\u041f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u044f \u0431\u044b \u0441\u0444\u043e\u0440\u043c\u0443\u043b\u0438\u0440\u043e\u0432\u0430\u043b \u0442\u0430\u043a: \u0445\u043e\u0442\u0438\u0442\u0435, \u0447\u0442\u043e\u0431\u044b \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u043f\u0435\u0440\u0435\u0440\u0435\u0441\u043e\u0432\u044b\u0432\u0430\u043b\u0441\u044f \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 useLayoutEffect; \u0435\u0441\u043b\u0438 \u0434\u043b\u044f \u0412\u0430\u0441 \u043d\u0435 \u0432\u0430\u0436\u043d\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043f\u043e\u0448\u0430\u0433\u043e\u0432\u043e\u0433\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f, \u0430 \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u0440\u0435\u043d\u0434\u0435\u0440\u0438\u0442\u044c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044e\u044e \u0432\u0435\u0440\u0441\u0438\u044e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f &#8212; useEffect. <\/p>\n<h2>\u0412\u0430\u0440\u0438\u0430\u043d\u0442 3. useState + useRef<\/h2>\n<p>\u042f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b useReducer, \u0442.\u043a. \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0434\u0432\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f: \u0448\u0430\u0433 \u0441\u0447\u0435\u0442\u0447\u0438\u043a\u0430 \u0438 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435. \u041d\u043e \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e useState. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 couterStep \u0431\u0443\u0434\u0435\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043e \u0442\u0430\u043a:<\/p>\n<pre><code class=\"javascript\">  const [counterStep, setCounterStep] = useState(0);   const counterStepStateRef = useRef(counterStep);   const setCounterStepState = (data) =&gt; {     counterStepStateRef.current = data;     setCounterStep(data);   };<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 useState + useRef<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">import { useEffect, useLayoutEffect, useRef, useState } from \"react\";  const PRELOADER_SELECTOR = \".preloader__wrapper\"; const PRELOADER_COUNTER_SELECTOR = \".preloader__counter\";  const usePreloader = () =&gt; {   const [counter, setCounter] = useState(0);   const counterStateRef = useRef(counter);   const setCounterState = (data) =&gt; {     counterStateRef.current = data;     setCounter(data);   };    const [counterStep, setCounterStep] = useState(0);   const counterStepStateRef = useRef(counterStep);   const setCounterStepState = (data) =&gt; {     counterStepStateRef.current = data;     setCounterStep(data);   };    const preloaderEl = document.querySelector(PRELOADER_SELECTOR);   const counterEl = document.querySelector(PRELOADER_COUNTER_SELECTOR);    const updateCounter = () =&gt; {     setCounterState(counterStateRef.current + counterStepStateRef.current);   };    const checkImageLoading = (url) =&gt; {     const imageChecker = new Image();     imageChecker.addEventListener(\"load\", updateCounter);     imageChecker.addEventListener(\"error\", updateCounter);     imageChecker.src = url;   };    useEffect(() =&gt; {     const imgArray = document.querySelectorAll(\"img\");     if (imgArray.length &gt; 0) {       setCounterStepState(Math.floor(100 \/ imgArray.length) + 1);       imgArray.forEach((img) =&gt; {         checkImageLoading(img.src);       });     }   }, []);    useLayoutEffect(() =&gt; {     if (counterEl) {       counterStateRef.current &lt; 100         ? (counterEl.innerHTML = `${counterStateRef.current}%`)         : hidePreloader(preloaderEl);     }   }, [counter, counterStep]);   return; };  const hidePreloader = (preloaderEl) =&gt; {   preloaderEl.remove(); };  export default usePreloader; <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h2>\u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0414\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u044d\u0442\u043e\u0433\u043e \u0445\u0443\u043a\u0430 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0435\u0433\u043e<\/p>\n<pre><code class=\"javascript\">export default function App() {   usePreloader();   return (     &lt;div className=\"App\"&gt;       &lt;Preloader \/&gt;       ...     &lt;\/div&gt;   ); }<\/code><\/pre>\n<\/p>\n<h2>\u0418\u0442\u043e\u0433\u043e:<\/h2>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u0441\u0442\u0430\u0442\u044c\u0438 \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e:<\/p>\n<ul>\n<li>\n<p>\u043a\u0430\u043a \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u0445\u0443\u043a<\/p>\n<\/li>\n<li>\n<p>\u043a\u0430\u043a \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435\u043c \u0445\u0443\u043a\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0445\u0443\u043a\u043e\u0432 useReducer \u0438 useState<\/p>\n<\/li>\n<li>\n<p>\u043a\u0430\u043a \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c useRef \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0441\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435<\/p>\n<\/li>\n<li>\n<p>\u0440\u0430\u0437\u043b\u0438\u0447\u0438\u0435 \u0432 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0438 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u0445\u0443\u043a\u043e\u0432 useEffect \u0438 useLayoutEffect<\/p>\n<\/li>\n<\/ul>\n<p>\u0421\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 <a href=\"https:\/\/codesandbox.io\/s\/preloader-tc2h1\" rel=\"noopener noreferrer nofollow\">\u043f\u0435\u0441\u043e\u0447\u043d\u0438\u0446\u0443<\/a><\/p>\n<p>\u0421\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 <a href=\"https:\/\/github.com\/donrus\/use-preloader.git\" rel=\"noopener noreferrer nofollow\">\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439<\/a><\/p>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u0438 \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u0442\u044c, \u0442.\u043a. \u0445\u0443\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442. \u041d\u043e \u044d\u0442\u043e\u0442 \u043f\u0440\u0435\u043b\u043e\u0430\u0434\u0435\u0440 \u043d\u0435\u043e\u0436\u0438\u0434\u0430\u043d\u043d\u043e \u043e\u043a\u0430\u0437\u0430\u043b\u0441\u044f \u043e\u0442\u043b\u0438\u0447\u043d\u044b\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u043c \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043f\u043e\u0442\u043e\u043a\u043e\u043c \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0438 \u0438\u0445 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u0430\u043c\u043e\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0435 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0447\u0430\u0441\u0442\u0438. <\/p>\n<p>\u041d\u0430\u0447\u043d\u0451\u043c, \u0430 \u0432\u0435\u0440\u043d\u0435\u0435 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u043c, \u0441 \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440\u0430\u043c\u0438&#8230;<\/p>\n<\/p>\n<\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/532440\/\"> https:\/\/habr.com\/ru\/post\/532440\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text_v2\" id=\"post-content-body\">\n<h2>\u041f\u0440\u0435\u043b\u044e\u0434\u0438\u044f<\/h2>\n<p>\u0421\u0442\u043e\u044f\u043b\u0430 \u0437\u0430\u0434\u0430\u0447\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u0435\u043b\u043e\u0430\u0434\u0435\u0440 \u0441 \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u043c \u043e\u0442\u0441\u0447\u0435\u0442\u043e\u043c \u043d\u0430 \u0440\u0435\u0430\u043a\u0442\u0435. \u0422.\u043a. \u0433\u0443\u0433\u043b\u0438\u0442\u044c \u043d\u0435 \u0443\u043c\u0435\u044e \u0438 \u043e\u0447\u0435\u043d\u044c \u043b\u044e\u0431\u043b\u044e \u043b\u0435\u043f\u0438\u0442\u044c \u0432\u0435\u043b\u043e\u0441\u0438\u043f\u0435\u0434\u044b (\u0445\u043e\u0440\u043e\u0448\u0438\u0439 \u0434\u0432\u0443\u0445\u043c\u0435\u0441\u0442\u043d\u044b\u0439 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0441\u044f \u0438\u0437 \u0434\u0432\u0443\u0445 \u0432\u0435\u043b\u043e\u0441\u0438\u043f\u0435\u0434\u043e\u0432 &#171;\u0423\u043a\u0440\u0430\u0438\u043d\u0430&#187;), \u0442\u043e \u044f \u0431\u044b\u043b \u043e\u0431\u0440\u0435\u0447\u0435\u043d \u0433\u043b\u0443\u0431\u043e\u043a\u043e \u043a\u043e\u043f\u0430\u0442\u044c \u043d\u0430 \u043f\u043e\u043b\u044f\u0445 \u0430\u0441\u0438\u043d\u0445\u0440\u0435\u043d\u044c\u0449\u0438\u043d\u044b. \u0417\u0430\u0431\u0435\u0433\u0443 \u043d\u0430\u043f\u0435\u0440\u0451\u0434 \u0438 \u0441\u043a\u0430\u0436\u0443, \u0447\u0442\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b \u044d\u0442\u043e\u0442 \u043f\u0440\u0435\u043b\u043e\u0430\u0434\u0435\u0440 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440\u043e\u0432, redux-saga, rxjs. \u041e\u0447\u0435\u043d\u044c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 \u043e\u043f\u044b\u0442 \u0438 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f, \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u044f, \u0447\u0442\u043e \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0442\u0435\u043b\u044c\u0441\u0442\u0432, \u043a\u0440\u043e\u043c\u0435 \u0441\u0442\u0430\u0442\u0435\u0439, \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0449\u0438\u0445 \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u0435 \u0432\u0435\u0449\u0438, \u0438 \u043e\u0431\u0440\u044b\u0432\u043a\u043e\u0432 \u0441\u0443\u0433\u0443\u0431\u043e \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043d\u0430 stackoverflow, \u043d\u0435 \u043d\u0430\u0445\u043e\u0434\u0438\u043b.<\/p>\n<p>\u0418\u0442\u0430\u043a: \u0447\u0430\u0441\u0442\u044c \u043f\u0435\u0440\u0432\u0430\u044f &#8212; \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u043e\u0433\u043e \u0445\u0443\u043a\u0430.<\/p>\n<h2>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u0435\u043b\u043e\u0430\u0434\u0435\u0440\u0430<\/h2>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0448\u043a\u0430\u043b\u044b \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438, \u044f \u0432\u044b\u0431\u0440\u0430\u043b \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a \u043d\u0430 \u0441\u0430\u0439\u0442\u0435 (\u043c\u043e\u0436\u043d\u043e \u0432\u044b\u0431\u0438\u0440\u0430\u0442\u044c \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u044b), \u0442.\u043a. \u0432 \u043c\u043e\u0451\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044d\u0442\u043e \u0431\u044b\u043b\u0438 \u0441\u0430\u043c\u044b\u0435 \u0442\u044f\u0436\u0451\u043b\u044b\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u044b \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b.<\/p>\n<p>\u0422.\u043a. \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0432 \u0440\u0435\u0430\u043a\u0442\u0435, \u0442\u043e \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435 \u043e\u0444\u043e\u0440\u043c\u0438\u043b \u044d\u0442\u043e \u0432 \u0432\u0438\u0434\u0435 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u043e\u0433\u043e \u0445\u0443\u043a\u0430 \u0438 \u043d\u0430\u0437\u0432\u0430\u043b \u0435\u0433\u043e usePreloader.<\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434 \u0441\u0430\u043c\u043e\u0433\u043e \u043f\u0440\u0435\u043b\u043e\u0430\u0434\u0435\u0440\u0430<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">import React from \"react\";  export default function Preloader() {   return (     &lt;div className=\"preloader__wrapper\"&gt;       &lt;div className=\"preloader\"&gt;         &lt;svg           width=\"300\"           height=\"300\"           viewBox=\"0 0 300 300\"           fill=\"none\"           xmlns=\"http:\/\/www.w3.org\/2000\/svg\"         &gt;           &lt;path             d=\"M300 150C300 232.843 232.843 300 150 300C67.1573 300 0 232.843 0 150C0 67.1573 67.1573 0 150 0C232.843 0 300 67.1573 300 150ZM20.7679 150C20.7679 221.373 78.6271 279.232 150 279.232C221.373 279.232 279.232 221.373 279.232 150C279.232 78.6271 221.373 20.7679 150 20.7679C78.6271 20.7679 20.7679 78.6271 20.7679 150Z\"             fill=\"#F3F3F3\"           \/&gt;           &lt;path             d=\"M289.616 150C295.351 150 300.037 154.655 299.641 160.376C297.837 186.392 289.275 211.553 274.72 233.336C258.238 258.003 234.811 277.229 207.403 288.582C179.994 299.935 149.834 302.906 120.736 297.118C91.6393 291.33 64.9119 277.044 43.934 256.066C22.9561 235.088 8.66999 208.361 2.88221 179.264C-2.90558 150.166 0.0649254 120.006 11.4181 92.5975C22.7712 65.1886 41.9971 41.7618 66.6645 25.2796C88.4468 10.725 113.608 2.16293 139.624 0.359232C145.345 -0.0374269 150 4.64906 150 10.384C150 16.1189 145.343 20.7246 139.627 21.1849C117.723 22.9486 96.5686 30.2756 78.2025 42.5475C56.9504 56.7477 40.3864 76.931 30.6052 100.545C20.8239 124.159 18.2647 150.143 23.2511 175.212C28.2375 200.28 40.5457 223.307 58.6191 241.381C76.6926 259.454 99.7195 271.762 124.788 276.749C149.857 281.735 175.841 279.176 199.455 269.395C223.069 259.614 243.252 243.05 257.453 221.797C269.724 203.431 277.051 182.277 278.815 160.373C279.275 154.657 283.881 150 289.616 150Z\"             fill=\"#6CAE30\"           \/&gt;         &lt;\/svg&gt;       &lt;\/div&gt;       &lt;div className=\"preloader__counter\"&gt;0%&lt;\/div&gt;     &lt;\/div&gt;   ); } <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0421\u0442\u0438\u043b\u0438 \u043f\u0440\u0435\u043b\u043e\u0430\u0434\u0435\u0440\u0430<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"css\">.App {   font-family: sans-serif;   text-align: center; }  .preloader__wrapper {   position: fixed;   display: flex;   flex-direction: column;   align-items: center;   justify-content: center;   top: 0;   left: 0;   width: 100%;   height: 100%;   background-color: #ffffff;   z-index: 2000; }  .preloader {   position: relative;   width: 18.75rem;   height: 18.75rem; } .preloader svg {   width: 18.75rem;   height: 18.75rem;   -webkit-animation: spin 4s infinite linear;   animation: spin 4s infinite linear; } .preloader__counter {   margin-top: 1.625rem;   padding-left: 1rem;   font-family: sans-serif;   font-size: 3.125rem;   line-height: 1;   color: #6cae30; }  @media (max-width: 900px) {   .preloader {     width: 6.25rem;     height: 6.25rem;   }   .preloader svg {     width: 6.25rem;     height: 6.25rem;   }   .preloader__counter {     margin-top: 0.75rem;     padding-left: 0.5rem;     font-size: 2.125rem;   } }  @-moz-keyframes spin {   from {     -moz-transform: rotate(0deg);   }   to {     -moz-transform: rotate(360deg);   } } @-webkit-keyframes spin {   from {     -webkit-transform: rotate(0deg);   }   to {     -webkit-transform: rotate(360deg);   } } @keyframes spin {   from {     transform: rotate(0deg);   }   to {     transform: rotate(360deg);   } } <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h2>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0445\u0443\u043a\u0430 <\/h2>\n<p>\u041b\u043e\u0433\u0438\u043a\u043e\u0439 \u0445\u0443\u043a\u0430 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u0438\u0441\u043a \u0432\u0441\u0435\u0445 \u0442\u044d\u0433\u043e\u0432 &lt;img&gt; \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u043d\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u044f load \u0438 error. \u0414\u0435\u043b\u0430\u0435\u0442\u0441\u044f \u044d\u0442\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u043a\u043e\u0434\u0430<\/p>\n<pre><code class=\"javascript\">const updateCounter = () =&gt; {   dispatch({     type: ACTIONS.SET_COUNTER,     data: state.counter + state.counterStep   }); };  const checkImageLoading = (url) =&gt; {   const imageChecker = new Image();   imageChecker.addEventListener(\"load\", updateCounter);   imageChecker.addEventListener(\"error\", updateCounter);   imageChecker.src = url; };<\/code><\/pre>\n<p>\u0423 \u0445\u0443\u043a\u0430 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 (state), \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0431\u0443\u0434\u0435\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u0448\u0430\u0433 \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u0438 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u0430. \u0412 \u0434\u0430\u043d\u043d\u043e\u0439 (\u043f\u0435\u0440\u0432\u043e\u0439) \u0447\u0430\u0441\u0442\u0438 \u043f\u0440\u0435\u043b\u043e\u0430\u0434\u0435\u0440 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0445\u0443\u043a\u043e\u0432 react, \u043d\u043e \u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0430\u0445. \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c redux \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u043d\u0435 \u0431\u0443\u0434\u0435\u043c, \u0442.\u043a. \u044d\u0442\u043e \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u0445\u0443\u043a \u0438 \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b \u0435\u0433\u043e \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c.<\/p>\n<details class=\"spoiler\">\n<summary>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 state.js<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">export const SET_COUNTER = \"SET_COUNTER\"; export const SET_COUNTER_STEP = \"SET_COUNTER_STEP\"; export const initialState = {   counter: 0,   counterStep: 0, }; export const reducer = (state, action) =&gt; {   switch (action.type) {     case SET_COUNTER:       return { ...state, counter: action.data };     case SET_COUNTER_STEP:       return { ...state, counterStep: action.data };     default:       throw new Error(\"This action is not applicable to this component.\");   } };  export const ACTIONS = {   SET_COUNTER,   SET_COUNTER_STEP, }; <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h2>\u0412\u0430\u0440\u0438\u0430\u043d\u0442 1 (\u043d\u0435 \u0440\u0430\u0431\u043e\u0447\u0438\u0439, \u0434\u043b\u044f \u043d\u0430\u0433\u043b\u044f\u0434\u043d\u043e\u0441\u0442\u0438). useReducer<\/h2>\n<p>\u042d\u0442\u043e\u0442 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u043f\u043e\u043a\u0430\u0437\u0430\u043d \u0434\u043b\u044f \u043d\u0430\u0433\u043b\u044f\u0434\u043d\u043e\u0441\u0442\u0438, \u0447\u0442\u043e\u0431\u044b \u0431\u044b\u043b\u043e \u043f\u043e\u043d\u044f\u0442\u043d\u043e, \u0432 \u043a\u0430\u043a\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u0431\u0435\u0437 useRef \u043d\u0435 \u043e\u0431\u043e\u0439\u0442\u0438\u0441\u044c. \u041c\u0435\u0442\u043e\u0434 <strong>checkImageLoading <\/strong>\u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0432 \u0446\u0438\u043a\u043b\u0435 \u043f\u0440\u0438 \u043c\u043e\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0438 \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 state \u0443 \u0432\u0441\u0435\u0445 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u0431\u0443\u0434\u0435\u0442 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u043d\u0430 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435.<\/p>\n<details class=\"spoiler\">\n<summary>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 usePreloader.js<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">import { useReducer, useEffect } from \"react\"; import { reducer, initialState, ACTIONS } from \".\/state\";  const PRELOADER_SELECTOR = \".preloader__wrapper\"; const PRELOADER_COUNTER_SELECTOR = \".preloader__counter\";  const usePreloader = () =&gt; {   const [state, dispatch] = useReducer(reducer, initialState);    const preloaderEl = document.querySelector(PRELOADER_SELECTOR);   const counterEl = document.querySelector(PRELOADER_COUNTER_SELECTOR);    const updateCounter = () =&gt; {     dispatch({       type: ACTIONS.SET_COUNTER,       data: state.counter + state.counterStep     });   };    const checkImageLoading = (url) =&gt; {     const imageChecker = new Image();     imageChecker.addEventListener(\"load\", updateCounter);     imageChecker.addEventListener(\"error\", updateCounter);     imageChecker.src = url;   };    useEffect(() =&gt; {     const imgArray = document.querySelectorAll(\"img\");     if (imgArray.length &gt; 0) {       dispatch({         type: ACTIONS.SET_COUNTER_STEP,         data: Math.floor(100 \/ imgArray.length) + 1       });       imgArray.forEach((img) =&gt; {         checkImageLoading(img.src);       });     }   }, []);    useEffect(() =&gt; {     if (counterEl) {       state.counter &lt; 100         ? (counterEl.innerHTML = `${state.counter}%`)         : hidePreloader(preloaderEl);     }   }, [state]);    return; };  const hidePreloader = (preloaderEl) =&gt; {   preloaderEl.remove(); };  export default usePreloader;<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h2>\u0412\u0430\u0440\u0438\u0430\u043d\u0442 2. useReducer + useRef<\/h2>\n<p>\u041e\u0431\u044a\u0435\u043a\u0442, \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0435\u043c\u044b\u0439 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e useRef \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u043d\u0430 \u043f\u0440\u043e\u0442\u044f\u0436\u0435\u043d\u0438\u0438 \u0432\u0441\u0435\u0433\u043e \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430. \u0412 \u043d\u0435\u0433\u043e \u044f \u0431\u0443\u0434\u0443 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0439 state \u0438 \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u0438\u0437 \u043a\u043e\u043b\u0431\u044d\u043a\u043e\u0432 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0445\u0443\u043a\u0430.<\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u044d\u0442\u043e\u0442 \u043e\u0431\u044a\u0435\u043a\u0442:<\/p>\n<pre><code class=\"javascript\">\/\/\u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044e \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 state const stateRef = useRef(state);<\/code><\/pre>\n<p>\u0412 \u043a\u043e\u043b\u0431\u044d\u043a\u0435 \u0441\u0441\u044b\u043b\u0430\u0442\u044c\u0441\u044f \u043d\u0430 state \u0447\u0435\u0440\u0435\u0437 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e current \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 stateRef:<\/p>\n<pre><code class=\"javascript\">const updateCounter = () =&gt; {   dispatch({     type: ACTIONS.SET_COUNTER,     data: stateRef.current.counter + stateRef.current.counterStep \/\/\u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u0435\u0440\u0443 \u043d\u0435 \u0438\u0437 state, \u0430 \u0438\u0437 stateRef   }); };<\/code><\/pre>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0441\u0441\u044b\u043b\u0430\u044e\u0441\u044c \u043d\u0430 state \u043f\u0440\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u0432 \u0440\u0430\u0437\u043c\u0435\u0442\u043a\u0435<\/p>\n<pre><code class=\"javascript\">stateRef.current.counter &lt; 100 \t? (counterEl.innerHTML = `${stateRef.current.counter}%`) \/\/\u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u0435\u0440\u0443 \u043d\u0435 \u0438\u0437 state, \u0430 \u0438\u0437 stateRef \t: hidePreloader(preloaderEl);<\/code><\/pre>\n<p>\u041f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 state, \u043d\u0443\u0436\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0443:<\/p>\n<pre><code class=\"javascript\">useEffect(() =&gt; {   stateRef.current = state; \/\/\u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 state, \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u044e \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0439 state   ... }, [state]);<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 useReducer + useRef<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">import { useReducer, useEffect, useRef } from \"react\"; import { reducer, initialState, ACTIONS } from \".\/state\";  const PRELOADER_SELECTOR = \".preloader__wrapper\"; const PRELOADER_COUNTER_SELECTOR = \".preloader__counter\";  const usePreloader = () =&gt; {   const [state, dispatch] = useReducer(reducer, initialState);    \/\/\u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044e \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 state   const stateRef = useRef(state);    const preloaderEl = document.querySelector(PRELOADER_SELECTOR);   const counterEl = document.querySelector(PRELOADER_COUNTER_SELECTOR);    const updateCounter = () =&gt; {     dispatch({       type: ACTIONS.SET_COUNTER,       data: stateRef.current.counter + stateRef.current.counterStep \/\/\u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u0435\u0440\u0443 \u043d\u0435 \u0438\u0437 state, \u0430 \u0438\u0437 stateRef     });   };    const checkImageLoading = (url) =&gt; {     const imageChecker = new Image();     imageChecker.addEventListener(\"load\", updateCounter);     imageChecker.addEventListener(\"error\", updateCounter);     imageChecker.src = url;   };    useEffect(() =&gt; {     const imgArray = document.querySelectorAll(\"img\");     if (imgArray.length &gt; 0) {       dispatch({         type: ACTIONS.SET_COUNTER_STEP,         data: Math.floor(100 \/ imgArray.length) + 1       });       imgArray.forEach((img) =&gt; {         checkImageLoading(img.src);       });     }   }, []);    useEffect(() =&gt; {     stateRef.current = state; \/\/\u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 state, \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u044e \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0439 state     if (counterEl) {       stateRef.current.counter &lt; 100         ? (counterEl.innerHTML = `${stateRef.current.counter}%`) \/\/\u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u0435\u0440\u0443 \u043d\u0435 \u0438\u0437 state, \u0430 \u0438\u0437 stateRef         : hidePreloader(preloaderEl);     }   }, [state]);    return; };  const hidePreloader = (preloaderEl) =&gt; {   preloaderEl.remove(); };  export default usePreloader; <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041d\u043e \u0442\u0443\u0442 \u043f\u043e\u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f<\/p>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-314672","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/314672","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=314672"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/314672\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=314672"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=314672"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=314672"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}