{"id":398137,"date":"2024-06-29T13:53:35","date_gmt":"2024-06-29T13:53:35","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=398137"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2024-06-29T13:53:35","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=398137","title":{"rendered":"<span>React: \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043e \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 \u0444\u043e\u0440\u043c\u0430\u043c\u0438<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/pu\/no\/zh\/punozh3zkth3cqyppf_przd5wco.png\" data-src=\"https:\/\/habrastorage.org\/webt\/pu\/no\/zh\/punozh3zkth3cqyppf_przd5wco.png\"\/>  <\/p>\n<p>  \u041f\u0440\u0438\u0432\u0435\u0442, \u0434\u0440\u0443\u0437\u044c\u044f!<\/p>\n<p>  <\/p>\n<p>\u0412 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435 \u0432\u0440\u0435\u043c\u044f \u043c\u043d\u0435 \u043d\u0430 \u0433\u043b\u0430\u0437\u0430 \u0447\u0430\u0441\u0442\u043e \u043f\u043e\u043f\u0430\u0434\u0430\u044e\u0442\u0441\u044f \u0441\u0442\u0430\u0442\u044c\u0438, \u043f\u043e\u0441\u0432\u044f\u0449\u0435\u043d\u043d\u044b\u0435 \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 \u0444\u043e\u0440\u043c\u0430\u043c\u0438 \u0432 <code>React<\/code> \u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0445 \u0445\u0443\u043a\u043e\u0432. \u0412\u043e\u0442 \u043f\u0430\u0440\u043e\u0447\u043a\u0430 \u0442\u0430\u043a\u0438\u0445 \u0441\u0442\u0430\u0442\u0435\u0439:<\/p>\n<p>  <\/p>\n<ul>\n<li><a href=\"https:\/\/daveceddia.com\/react-forms\/\">Using Forms in React<\/a><\/li>\n<li><a href=\"https:\/\/habr.com\/ru\/post\/523256\/\">React.js \u2014 \u0444\u043e\u0440\u043c\u043e\u0448\u043b\u0435\u043f\u0441\u0442\u0432\u043e \u0438\u043b\u0438 \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 \u0444\u043e\u0440\u043c\u0430\u043c\u0438 \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u0445\u0443\u043a\u043e\u0432<\/a><\/li>\n<li><a href=\"https:\/\/felixgerschau.com\/react-hooks-form-validation-typescript\/\">Form validation with React Hooks WITHOUT a library: The Complete Guide<\/a><\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u042f \u0442\u043e\u0436\u0435 \u0440\u0435\u0448\u0438\u043b \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0432 \u044d\u0442\u043e\u043c \u0441\u0432\u043e\u0438 \u0441\u0438\u043b\u044b, \u0447\u0435\u043c\u0443 \u0438 \u043f\u043e\u0441\u0432\u044f\u0449\u0435\u043d\u0430 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f. \u041f\u043e\u0434 &#171;\u044d\u0442\u0438\u043c&#187; \u044f \u043f\u043e\u0434\u0440\u0430\u0437\u0443\u043c\u0435\u0432\u0430\u044e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u043e\u0433\u043e \u0445\u0443\u043a\u0430 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0444\u043e\u0440\u043c\u0430\u043c\u0438 \u0432 <code>React<\/code>.<\/p>\n<p>  <\/p>\n<p>\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043c\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u0445\u0443\u043a, \u0437\u0430\u0442\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0435\u0433\u043e \u0432 \u0444\u043e\u0440\u043c\u0430\u0445 \u0434\u043b\u044f \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.<\/p>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0435\u0439 \u043f\u0440\u0430\u0432\u0434\u043e\u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0441\u0442\u0438 \u043c\u044b \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 express-\u0441\u0435\u0440\u0432\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, jwt-\u0442\u043e\u043a\u0435\u043d \u0438 \u0445\u0435\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043f\u0430\u0440\u043e\u043b\u044c), \u0430 \u0442\u0430\u043a\u0436\u0435 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u0438\u043f\u0438\u0447\u043d\u044b\u0435 \u0434\u043b\u044f \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043e\u0448\u0438\u0431\u043a\u0438 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <code>404 User not found<\/code> \u0438\u043b\u0438 <code>409 Email already in use<\/code>).<\/p>\n<p>  <\/p>\n<p>\u041f\u0440\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0445\u0443\u043a\u0430 \u0438 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043c\u044b \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u043c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044b\u0445 \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435, \u0441\u043c\u0435\u044e \u043d\u0430\u0434\u0435\u044f\u0442\u044c\u0441\u044f, \u043f\u043e\u043a\u0430\u0436\u0443\u0442\u0441\u044f \u0432\u0430\u043c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u043c\u0438.<\/p>\n<p>  <\/p>\n<p>\u041f\u0435\u0441\u043e\u0447\u043d\u0438\u0446\u0430:<\/p>\n<p>  <\/p>\n<div class=\"oembed\">\n<div class=\"tm-iframe_temp\" data-src=\"https:\/\/embedd.srv.habr.com\/iframe\/6120c7c2dc7e91a24fcac46e\" data-style=\"\" id=\"6120c7c2dc7e91a24fcac46e\" width=\"\"><\/div>\n<\/div>\n<p>  <\/p>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0432 \u043f\u0435\u0441\u043e\u0447\u043d\u0438\u0446\u0435 \u0432\u0441\u0435 \u0437\u0430\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e, \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0432\u0432\u0435\u0441\u0442\u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u0443 <code>yarn dev<\/code> \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0435.<\/p>\n<p>  <\/p>\n<p>\u0425\u0443\u043a \u0432 \u0444\u043e\u0440\u043c\u0435 npm-\u043f\u0430\u043a\u0435\u0442\u0430 \u2014 <a href=\"https:\/\/www.npmjs.com\/package\/simple-form-react\"><code>simple-form-react<\/code><\/a>.<\/p>\n<p><a name=\"habracut\"><\/a>  <\/p>\n<h2 id=\"razrabotka-huka\">\u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0445\u0443\u043a\u0430<\/h2>\n<p>  <\/p>\n<p><em>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/em>: \u0445\u0443\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0441 \u0432\u0430\u043c\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c, \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0444\u043e\u0440\u043c\u0430\u043c\u0438 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u0438, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0441\u0440\u0435\u0434\u043d\u0438\u0445 \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u0432. \u0415\u0441\u043b\u0438 \u0432 \u0432\u0430\u0448\u0430 \u0444\u043e\u0440\u043c\u0430 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 100 \u043f\u043e\u043b\u0435\u0439 (\u0443\u0441\u043b\u043e\u0432\u043d\u043e), \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e, \u0432\u0430\u043c \u043b\u0443\u0447\u0448\u0435 \u043f\u0440\u0438\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c\u0441\u044f \u043a <a href=\"https:\/\/react-hook-form.com\/\">React Hook Form<\/a>, <a href=\"https:\/\/formik.org\/docs\/overview\">Formik<\/a> \u0438\u043b\u0438 \u0434\u0440\u0443\u0433\u0438\u043c \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u044b\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u044f\u043c.<\/p>\n<p>  <\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043d\u0430\u0447\u043d\u0435\u043c \u0441 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0442\u043e\u0433\u043e, \u043a\u0430\u043a\u0438\u0435 \u043f\u0440\u043e\u043f\u044b \u0434\u043e\u043b\u0436\u0435\u043d \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u043d\u0430\u0448 \u0445\u0443\u043a.<\/p>\n<p>  <\/p>\n<p>\u041d\u0435\u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e, \u043a\u0430\u043a \u0441\u043f\u0440\u0430\u0432\u0435\u0434\u043b\u0438\u0432\u043e \u043e\u0442\u043c\u0435\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u0441\u0442\u0430\u0442\u044c\u0435 <code>Using Forms in React<\/code>, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0435\u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c\u044b\u0445 &#171;\u0438\u043d\u043f\u0443\u0442\u043e\u0432&#187; (\u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u043a\u0430\u043a \u0447\u0435\u0440\u0435\u0437 \u0440\u0435\u0444\u044b, \u0442\u0430\u043a \u0438 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e), \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e, \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c, \u0447\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c\u044b\u0445 \u0438\u043d\u043f\u0443\u0442\u043e\u0432, \u043d\u0430\u0448\u0430 \u0444\u043e\u0440\u043c\u0430 \u0431\u0443\u0434\u0435\u0442 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c\u043e\u0439, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0435\u0440\u0432\u044b\u043c \u043f\u0440\u043e\u043f\u043e\u043c, \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u043c\u044b\u043c \u0445\u0443\u043a\u043e\u043c, \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u043d\u043f\u0443\u0442\u043e\u0432. \u041d\u0430\u0437\u043e\u0432\u0435\u043c \u0438\u0445 <code>initialData<\/code>.<\/p>\n<p>  <\/p>\n<p>\u0424\u043e\u0440\u043c\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u043a\u0443\u0434\u0430-\u0442\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c\u0441\u044f (\u0438\u043c\u0435\u0435\u0442\u0441\u044f \u0432 \u0432\u0438\u0434\u0443 \u043a\u043e\u043d\u0435\u0447\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435), \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u0442\u043e\u0440\u044b\u043c \u043f\u0440\u043e\u043f\u043e\u043c \u0431\u0443\u0434\u0435\u0442 <code>url<\/code>.<\/p>\n<p>  <\/p>\n<p>\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u043c\u0435\u0442\u043e\u0434\u043e\u043c <code>POST<\/code>, \u043d\u043e \u0443 \u043d\u0430\u0441 \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u044d\u0442\u043e \u0438\u0437\u043c\u0435\u043d\u044f\u0442\u044c. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043f\u0440\u043e\u043f\u043e\u043c \u0431\u0443\u0434\u0435\u0442 <code>method<\/code> \u0441 \u0434\u0435\u0444\u043e\u043b\u0442\u043d\u044b\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c <code>POST<\/code>.<\/p>\n<p>  <\/p>\n<p>\u0418\u043d\u043f\u0443\u0442\u044b \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043a\u0430\u043a \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438, \u0442\u0430\u043a \u0438 \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u043c\u0438, \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e, \u043d\u0430\u043c \u043d\u0443\u0436\u0435\u043d \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 \u0442\u043e\u0433\u043e, \u0432\u0441\u0435 \u043b\u0438 \u0438\u043d\u043f\u0443\u0442\u044b \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <code>required<\/code> \u0441 \u0434\u0435\u0444\u043e\u043b\u0442\u043d\u044b\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c <code>true<\/code>. \u042d\u0442\u043e \u0447\u0435\u0442\u0432\u0435\u0440\u0442\u044b\u0439 \u043f\u0440\u043e\u043f \u043d\u0430\u0448\u0435\u0433\u043e \u0445\u0443\u043a\u0430.<\/p>\n<p>  <\/p>\n<p>\u041d\u0430\u0448 \u0445\u0443\u043a \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0447\u0442\u0438 \u0431\u0435\u0437\u0433\u043e\u043b\u043e\u0432\u044b\u043c (headless). \u042d\u0442\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0431\u0430\u0437\u043e\u0432\u0443\u044e \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e \u0438\u043d\u043f\u0443\u0442\u043e\u0432, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u043e\u0431\u0435\u0437\u0437\u0430\u0440\u0430\u0436\u0438\u0432\u0430\u043d\u0438\u0435 (sanitizing) \u0434\u0430\u043d\u043d\u044b\u0445, \u0432\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c, \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u043e\u043b\u0435\u0439. \u0412\u043c\u0435\u0441\u0442\u0435 \u0441 \u0442\u0435\u043c \u0445\u0443\u043a \u0434\u043e\u043b\u0436\u0435\u043d \u0438\u043c\u0435\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u0430\u043c\u0438 \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0438\u0445 \u043d\u0430\u0434 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c\u0438 \u0438\u043d\u043f\u0443\u0442\u0430\u043c\u0438. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u043f <code>validators<\/code>.<\/p>\n<p>  <\/p>\n<p>\u0422\u043e\u0442 \u0444\u0430\u043a\u0442, \u0447\u0442\u043e \u0445\u0443\u043a \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u0430\u043c\u0438, \u043e\u0431\u0443\u0441\u043b\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u044c \u043d\u0430\u043b\u0438\u0447\u0438\u044f \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0441 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u043c\u0438 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0430\u0445 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435, \u043a\u043e\u0433\u0434\u0430 \u0438\u043d\u043f\u0443\u0442 \u043f\u043e\u0441\u043b\u0435 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u043f\u0440\u0438\u0437\u043d\u0430\u0435\u0442\u0441\u044f \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u044b\u043c. \u0428\u0435\u0441\u0442\u043e\u0439 \u043f\u0440\u043e\u043f \u2014 <code>messages<\/code>.<\/p>\n<p>  <\/p>\n<p>\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0445\u0443\u043a \u0431\u0443\u0434\u0435\u0442 \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0438\u043d\u043f\u0443\u0442\u0430\u043c\u0438 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u043e\u0439 \u0444\u043e\u0440\u043c\u044b \u043f\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u043c\u0443 <code>url<\/code>. \u041e\u0434\u043d\u0430\u043a\u043e \u0443 \u043d\u0430\u0441 \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u044f\u0442\u044c \u044d\u0442\u043e \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435. \u0421\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e, \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u044b \u0435\u0449\u0435 \u0434\u0432\u0430 \u043f\u0440\u043e\u043f\u0430 \u0434\u043b\u044f \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u2014 <code>onChange<\/code> \u0438 <code>onSubmit<\/code>.<\/p>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0444\u043e\u0440\u043c\u044b \u0445\u0443\u043a \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043d\u0443\u044e \u043c\u043d\u043e\u0439 \u0443\u0442\u0438\u043b\u0438\u0442\u0443 <a href=\"https:\/\/www.npmjs.com\/package\/very-simple-fetch\"><code>very-simple-fetch<\/code><\/a>, \u043e \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u044f \u043f\u0438\u0441\u0430\u043b \u0432 <a href=\"https:\/\/habr.com\/ru\/company\/timeweb\/blog\/571252\/\">\u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435<\/a>. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u0448 \u0445\u0443\u043a \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u0435\u0449\u0435 \u043e\u0434\u0438\u043d \u043f\u0440\u043e\u043f \u2014 \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 \u0434\u043b\u044f \u043d\u0430\u0437\u0432\u0430\u043d\u043d\u043e\u0439 \u0443\u0442\u0438\u043b\u0438\u0442\u044b. \u041d\u0430\u0437\u043e\u0432\u0435\u043c \u044d\u0442\u043e\u0442 \u043f\u0440\u043e\u043f <code>fetchOptions<\/code>.<\/p>\n<p>  <\/p>\n<p>\u041f\u0440\u0438\u0441\u0442\u0443\u043f\u0430\u0435\u043c \u043a \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438.<\/p>\n<p>  <\/p>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u043e\u0431\u0435\u0437\u0437\u0430\u0440\u0430\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0432\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0438\u0437 \u043d\u0438\u0445 \u043b\u0438\u0448\u043d\u0438\u0445 \u043f\u0440\u043e\u0431\u0435\u043b\u043e\u0432 \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">\/\/ \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0441\u0442\u0440\u043e\u043a\u0443 export const escapeAndTrim = (str) =>  str    .replace(\/[&lt;>&amp;'\"\/]\/g, '')    .replace(\/\\s{2,}\/g, ' ')    .trim()<\/code><\/pre>\n<p>  <\/p>\n<p>\u0410 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u043e\u043b\u0435\u0439 \u0442\u0430\u043a:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">\/\/ \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043e\u0431\u044a\u0435\u043a\u0442 \/\/ \u043f\u043e\u043b\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0431\u0443\u0434\u0443\u0442 \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442\u044c \u0447\u0435\u0440\u0435\u0437 `escapeAndTrim()` export const isEmpty = (fields) => Object.values(fields).some((field) => !field)<\/code><\/pre>\n<p>  <\/p>\n<p>\u041d\u0430\u0447\u0438\u043d\u0430\u0435\u043c \u043f\u0438\u0441\u0430\u0442\u044c \u0445\u0443\u043a:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">export default function useSimpleForm({  initialData,  url,  method = 'POST',  required = true,  validators,  messages,  onChange,  onSubmit,  fetchOptions }) {  \/\/ \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0445\u0443\u043a\u0430 }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043d\u044f\u0442\u044c, \u043a\u0430\u043a\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u0434\u043e\u043b\u0436\u0435\u043d \u0438\u043d\u043a\u0430\u043f\u0441\u0443\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0430\u0448 \u0445\u0443\u043a, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0441 \u0442\u0435\u043c, \u0447\u0442\u043e \u043e\u043d \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c.<\/p>\n<p>  <\/p>\n<p>\u041f\u0435\u0440\u0432\u043e\u0435, \u0447\u0442\u043e \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 \u043d\u0430 \u0443\u043c, \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c\u043e\u0441\u0442\u044c \u043d\u0430\u0448\u0435\u0439 \u0444\u043e\u0440\u043c\u044b, \u2014 \u044d\u0442\u043e \u043e\u0431\u044a\u0435\u043a\u0442 \u0441\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u0438\u043d\u043f\u0443\u0442\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e \u0444\u043e\u0440\u043c\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0442\u044c \u0441 \u043f\u0440\u043e\u043f\u043e\u043c <code>initialData<\/code>. \u041d\u0430\u0437\u043e\u0432\u0435\u043c \u044d\u0442\u043e\u0442 \u043e\u0431\u044a\u0435\u043a\u0442 <code>fields<\/code>.<\/p>\n<p>  <\/p>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u0438 \u0434\u043b\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0438\u043d\u043f\u0443\u0442\u043e\u0432 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0444\u043e\u0440\u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u0438\u0437 \u0444\u043e\u0440\u043c\u044b, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0445\u0443\u043a \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043e\u0431\u0435 \u044d\u0442\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u2014 <code>change<\/code> \u0438 <code>submit<\/code>.<\/p>\n<p>  <\/p>\n<p>\u0422\u0430\u043a\u0436\u0435 \u043d\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u0441\u0431\u0440\u043e\u0441\u0430 \u0444\u043e\u0440\u043c\u044b \u2014 <code>reset<\/code>. \u0412\u044b \u043f\u043e\u0439\u043c\u0435\u0442\u0435, \u043f\u043e\u0447\u0435\u043c\u0443 \u043d\u0430\u043c \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043d\u0430\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 <a href=\"https:\/\/developer.mozilla.org\/ru\/docs\/Web\/API\/HTMLFormElement\/reset\"><code>HTMLFormElement.reset<\/code><\/a>, \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u0434\u043e\u0439\u0434\u0435\u043c \u0434\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u044d\u0442\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438.<\/p>\n<p>  <\/p>\n<p>\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0445\u0443\u043a \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u043e\u043b\u0435\u0439, \u043d\u0430\u043c \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440. \u041a\u0430\u043a \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e\u0442 \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 (\u044d\u0442\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442 \u0435\u0433\u043e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435)? \u0421\u0430\u043c\u044b\u0439 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u2014 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u0434\u043b\u044f \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a\u043d\u043e\u043f\u043a\u0438 \u0434\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0444\u043e\u0440\u043c\u044b. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u0430\u043d\u043d\u044b\u0439 \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f <code>disabled<\/code>.<\/p>\n<p>  <\/p>\n<p>\u041e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u0444\u043e\u0440\u043c\u044b \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442 \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443. \u041e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u043a <code>API<\/code> \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442 \u043a\u0430\u043a\u043e\u0435-\u0442\u043e \u0432\u0440\u0435\u043c\u044f. \u0427\u0435\u043c \u0434\u043e\u043b\u0436\u0435\u043d \u0437\u0430\u043d\u0438\u043c\u0430\u0442\u044c\u0441\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432 \u044d\u0442\u043e \u0432\u0440\u0435\u043c\u044f? \u041a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435, \u043b\u044e\u0431\u043e\u0432\u0430\u0442\u044c\u0441\u044f &#171;\u043b\u043e\u0430\u0434\u0435\u0440\u043e\u043c&#187;. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0443\u0436\u0435\u043d \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 \u2014 <code>loading<\/code>.<\/p>\n<p>  <\/p>\n<p>\u041d\u0443 \u0438 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435 \u043f\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0443, \u043d\u043e \u043d\u0435 \u043f\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044e, \u0447\u0442\u043e \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043d\u0430\u0448 \u0445\u0443\u043a, \u2014 \u044d\u0442\u043e \u043e\u0431\u044a\u0435\u043a\u0442 \u043e\u0442\u0432\u0435\u0442\u0430 \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 <code>response<\/code> \u0438 \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u043e\u0448\u0438\u0431\u043a\u0430\u043c\u0438 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 <code>errors<\/code>.<\/p>\n<p>  <\/p>\n<p><em>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/em>: \u0443\u0442\u0438\u043b\u0438\u0442\u0430 <code>very-simple-fetch<\/code> \u0441\u043f\u0440\u043e\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0430 \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0447\u0442\u043e \u0432 \u043e\u0431\u044a\u0435\u043a\u0442 \u043e\u0442\u0432\u0435\u0442\u0430 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0443\u0441\u043f\u0435\u0448\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442 (\u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>data<\/code>), \u043d\u043e \u0442\u0430\u043a\u0436\u0435 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u0438 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0435 \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 <code>res.status(400).json({ message: 'Custom error' })<\/code> \u0438\u043b\u0438 <code>throw new Error('Exception')<\/code>; \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>error<\/code>). \u0422\u0430\u043a\u0436\u0435 \u043e\u0431\u044a\u0435\u043a\u0442 \u043e\u0442\u0432\u0435\u0442\u0430 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e, \u0432 \u0447\u0430\u0441\u0442\u043d\u043e\u0441\u0442\u0438 \u0441\u0442\u0430\u0442\u0443\u0441-\u043a\u043e\u0434 \u043e\u0442\u0432\u0435\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0430 \u043e\u0448\u0438\u0431\u043a\u0438, \u0432\u043e\u0437\u043d\u0438\u043a\u0448\u0435\u0439 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435, \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f (\u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>info<\/code>).<\/p>\n<p>  <\/p>\n<p>\u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043e\u0431\u044a\u0435\u043a\u0442 <code>errors<\/code>, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0439 \u0445\u0443\u043a\u043e\u043c, \u2014 \u044d\u0442\u043e \u043e\u0448\u0438\u0431\u043a\u0438, \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0441 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0435\u0439 \u0438\u043d\u043f\u0443\u0442\u043e\u0432.<\/p>\n<p>  <\/p>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0441\u0442\u0440\u043e\u043a\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0445\u0443\u043a\u0430 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">return { fields, change, submit, reset, disabled, loading, response, errors }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0445\u0443\u043a\u0430 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e 2 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 React-\u0445\u0443\u043a\u0430. \u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u0438\u0445 \u0432 \u043d\u0430\u0447\u0430\u043b\u0435 \u0444\u0430\u0439\u043b\u0430:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import { useState, useEffect } from 'react'<\/code><\/pre>\n<p>  <\/p>\n<p>\u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c <code>very-simple-fetch<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">yarn add very-simple-fetch # or npm i very-simple-fetch<\/code><\/pre>\n<p>  <\/p>\n<p>\u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u0435\u0435 \u043f\u043e\u0441\u043b\u0435 React-\u0445\u0443\u043a\u043e\u0432 \u0438 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u0434\u043b\u044f \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0435\u043d\u0438\u044f \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043f\u0440\u044f\u043c\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f (\u043c\u044b \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u044d\u0442\u0438\u043c \u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f <code>URL<\/code> \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u0435\u0442\u0442\u0435\u0440\u0430 <code>simpleFetch.baseUrl<\/code>):<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import simpleFetch from 'very-simple-fetch' export { simpleFetch }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u043e\u0432 (<code>validators<\/code>) \u043d\u0430\u0434 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c\u0438 \u0438\u043d\u043f\u0443\u0442\u0430\u043c\u0438 \u0441 \u0432\u044b\u0431\u043e\u0440\u043e\u043c \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0430\u0445 (<code>messages<\/code>) \u043d\u0430\u043c \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043e\u0431\u0449\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438. \u042d\u0442\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043b\u0443\u0447\u0448\u0435 \u0432\u044b\u043d\u0435\u0441\u0442\u0438 \u0437\u0430 \u043f\u0440\u0435\u0434\u0435\u043b\u044b \u0445\u0443\u043a\u0430, \u0432\u043e \u0438\u0437\u0431\u0435\u0436\u0430\u043d\u0438\u0435 \u0435\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433\u0435. \u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u043c\u044b \u043c\u043e\u0433\u043b\u0438 \u0431\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u043c\u0435\u043c\u043e\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0435\u0435 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>useCallback<\/code> \u0441 \u043f\u0443\u0441\u0442\u044b\u043c \u043c\u0430\u0441\u0441\u0438\u0432\u043e\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439, \u043d\u043e \u044d\u0442\u043e \u043f\u0440\u0438\u0432\u0435\u043b\u043e \u0431\u044b \u043a \u043d\u0435\u043e\u043f\u0440\u0430\u0432\u0434\u0430\u043d\u043d\u044b\u043c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u0440\u0430\u0441\u0445\u043e\u0434\u0430\u043c \u043d\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c.<\/p>\n<p>  <\/p>\n<p>\u0420\u0430\u0437\u043c\u044b\u0448\u043b\u044f\u044f \u043e \u0441\u0438\u0433\u043d\u0430\u0442\u0443\u0440\u0435 \u0434\u0430\u043d\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u044f \u043f\u043e\u0441\u0447\u0438\u0442\u0430\u043b \u0445\u043e\u0440\u043e\u0448\u0435\u0439 \u0438\u0434\u0435\u0435\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u043d\u0435\u0439 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u043f\u043e\u043b\u0435\u0439 \u0441 \u0438\u0445 \u043f\u0430\u0440\u0430\u043c\u0438 \u0434\u043b\u044f \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <code>password<\/code> \u0438 <code>confirmPassword<\/code>). \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0442\u0430\u043a\u0436\u0435 \u0434\u043e\u043b\u0436\u043d\u0430 \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c, \u0447\u0442\u043e \u043f\u043e\u043b\u044f, \u0434\u043b\u044f \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0432 <code>validators<\/code> \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u044b \u0438 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435 \u0438\u043c\u0435\u044e\u0442 \u043f\u0430\u0440 \u0434\u043b\u044f \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f, \u0432\u0441\u0435\u0433\u0434\u0430 \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0432\u0430\u043b\u0438\u0434\u043d\u044b\u043c\u0438. \u0412\u043e\u0442 \u043a\u0430\u043a \u043e\u043d\u0430 \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">\/\/ \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043f\u043e\u043b\u044f, \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u044b \u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0430\u0445 const validate = (fields, validators, messages) => {  \/\/ \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0432\u0430\u043b\u0438\u0434\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u043b\u044f, \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u043e\u043b\u044f \u0438 \u0435\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435  const isValid = (key, value) =>    \/\/ \u0434\u043b\u044f \u043f\u043e\u043b\u044f \u043d\u0435\u0442 \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u0430, \u0438 \u043e\u043d\u043e \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u0430\u0440\u043e\u0439 \u0434\u043b\u044f \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f    (!validators[key] &amp;&amp; !key.includes('confirm')) ||    \/\/ \u0434\u043b\u044f \u043f\u043e\u043b\u044f \u0435\u0441\u0442\u044c \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440, \u0438 \u043e\u043d\u043e \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u044d\u0442\u043e\u0433\u043e \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u0430    (validators[key] &amp;&amp; validators[key](value)) ||    \/\/ \u043f\u043e\u043b\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u0430\u0440\u043e\u0439 \u0434\u043b\u044f \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f, \u0438 \u0435\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0435\u0442 \u0441\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c \u043f\u0430\u0440\u044b - \u043e\u0434\u043d\u043e\u0438\u043c\u0435\u043d\u043d\u043e\u0433\u043e (\u0431\u0435\u0437 \u0443\u0447\u0435\u0442\u0430 `confirm`) \u043f\u043e\u043b\u044f    (key.includes('confirm') &amp;&amp;      value === fields[key.replace('confirm', '').toLowerCase()])   \/\/ \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u043e\u0448\u0438\u0431\u043a\u0430\u043c\u0438  return Object.entries(fields).reduce((errors, [key, value]) => {    \/\/ \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u0435 \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u0430\u043b\u0438\u0434\u043d\u044b\u043c    if (!isValid(key, value)) {      \/\/ \u0435\u0441\u043b\u0438 \u0434\u043b\u044f \u043f\u043e\u043b\u044f \u0438\u043c\u0435\u0435\u0442\u0441\u044f \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435, \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u0435\u0433\u043e \u0432 \u043e\u0434\u043d\u043e\u0438\u043c\u0435\u043d\u043d\u043e\u0435 \u043f\u043e\u043b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0441 \u043e\u0448\u0438\u0431\u043a\u0430\u043c\u0438      \/\/ \u0438\u043d\u0430\u0447\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0430\u0435\u043c \u043e\u0448\u0438\u0431\u043a\u0443 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f      errors[key] = messages[key] || true    }    return errors  }, {}) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0435\u0441\u043b\u0438 <code>validators<\/code> \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 <code>email: isEmail<\/code>, \u0430 <code>messages<\/code> \u2014 <code>email: 'Wrong email!'<\/code>, \u043f\u0440\u0438 \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u043b\u044f <code>email<\/code> \u0432 \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u043e\u0448\u0438\u0431\u043a\u0430\u043c\u0438 \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043f\u0438\u0441\u0430\u043d\u043e <code>email: 'Wrong email!'<\/code>. \u0415\u0441\u043b\u0438 \u0438\u043c\u0435\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u0435 <code>confirmPassword<\/code> \u0438 \u0435\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043d\u0435 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0435\u0442 \u0441\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c \u043f\u043e\u043b\u044f <code>password<\/code>, \u0430 <code>messages<\/code> \u043d\u0435 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u043f\u043e\u043b\u0435 <code>confirmPassword<\/code>, \u0442\u043e \u0432 <code>errors<\/code> \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043f\u0438\u0441\u0430\u043d\u043e <code>confitmPassword: true<\/code>.<\/p>\n<p>  <\/p>\n<p>\u0417\u0434\u0435\u0441\u044c \u0436\u0435 \u0438\u043c\u0435\u0435\u0442 \u0441\u043c\u044b\u0441\u043b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u043e\u043b\u0435\u0439 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435, \u043a\u043e\u0433\u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 <code>required<\/code> \u0438\u043c\u0435\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>false<\/code>, \u0442.\u0435. \u043a\u043e\u0433\u0434\u0430 \u043e\u0434\u043d\u0438 \u043f\u043e\u043b\u044f \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438, \u0430 \u0434\u0440\u0443\u0433\u0438\u0435 \u2014 \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u043c\u0438:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">const getRequiredFields = (target) =>  [...target.closest('form').querySelectorAll('input[required]')].reduce(    (obj, { name, value }) => {      obj[name] = value      return obj    },    {}  )<\/code><\/pre>\n<p>  <\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u043f\u043e-\u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u043c\u0443 \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u0442\u044c \u043a \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438.<\/p>\n<p>  <\/p>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">\/\/ \u0432\u043d\u0443\u0442\u0440\u0438 `useSimpleForm()`  \/\/ \u0432\u0441\u0435 \u043f\u043e\u043b\u044f const [fields, setFields] = useState(initialData) \/\/ \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u043b\u044f \/\/ \u043d\u0430 \u0434\u0430\u043d\u043d\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u043c\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u043c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c, \u043a\u0430\u043a\u0438\u0435 \u043f\u043e\u043b\u044f \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0442\u0430\u043a\u043e\u0432\u044b\u043c\u0438 const [requiredFields, setRequiredFields] = useState(null) \/\/ \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u043d\u0435\u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0445\u043e\u0442\u044f \u0431\u044b \u043e\u0434\u043d\u043e\u0433\u043e \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044f \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u0435\u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u043c \/\/ \u044d\u0442\u043e \u0441\u0432\u044f\u0437\u0430\u043d\u043e \u0441 \"\u043d\u0430 \u0434\u0430\u043d\u043d\u043e\u043c \u044d\u0442\u0430\u043f\u0435...\" const [disabled, setDisabled] = useState(true) const [loading, setLoading] = useState(false)  const [response, setResponse] = useState(null) \/\/ \u0434\u043e\u043f\u0443\u0441\u043a\u0430\u0435\u043c, \u0447\u0442\u043e \u043f\u043e\u0442\u0435\u043d\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u043b\u044e\u0431\u043e\u0435 \u043f\u043e\u043b\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u044b\u043c, \/\/ \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0432\u0441\u0435 \u0438\u043b\u0438 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e \u043f\u043e\u043b\u0435\u0439 \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438 const [errors, setErrors] = useState(initialData)<\/code><\/pre>\n<p>  <\/p>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u043e\u0431\u043e\u0447\u043d\u043e\u0433\u043e \u044d\u0444\u0444\u0435\u043a\u0442\u0430 \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043b\u044e\u0431\u043e\u0433\u043e \u043f\u043e\u043b\u044f \u043c\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u043e\u043b\u0435\u0439 \u0438 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u044d\u0442\u043e\u0433\u043e \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 <code>disabled<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">useEffect(() => {  \/\/ \u0435\u0441\u043b\u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u043d\u043f\u0443\u0442\u044b \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438  if (!required &amp;&amp; requiredFields) {    setDisabled(isEmpty(requiredFields))  \/\/ \u0435\u0441\u043b\u0438 \u0432\u0441\u0435 \u0438\u043d\u043f\u0443\u0442\u044b \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u0438\u043b\u0438 \u043c\u044b \u0435\u0449\u0435 \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u043b\u044f  } else {    setDisabled(isEmpty(fields))  } }, [required, fields, requiredFields])<\/code><\/pre>\n<p>  <\/p>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044f:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">const change = (e) => {  \/\/ \u0435\u0441\u043b\u0438 \u043d\u0435 \u0432\u0441\u0435 \u043f\u043e\u043b\u044f \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438, \u0438 \u043c\u044b \u0435\u0449\u0435 \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u043b\u0438 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u043e\u043b\u0435\u0439  if (!required &amp;&amp; !requiredFields) {    setRequiredFields(getRequiredFields(e.target))  }   \/\/ \u0441\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442 \u043e\u0442\u0432\u0435\u0442\u0430  setResponse(null)  \/\/ \u0441\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u043e\u0448\u0438\u0431\u043a\u0430\u043c\u0438  setErrors(initialData)   \/\/ \u0435\u0441\u043b\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u0430 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f, \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u0435\u0435 \u0441 \u0441\u043e\u0431\u044b\u0442\u0438\u0435\u043c \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430  if (typeof onChange === 'function') {    return onChange(e)  }   \/\/ \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u043e\u043b\u044f \u0438 \u0435\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0438\u0437 \u0446\u0435\u043b\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f  const { name, value } = e.target   \/\/ \u0435\u0441\u043b\u0438 \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u043e\u0441\u044c \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u0435  if (requiredFields &amp;&amp; requiredFields.hasOwnProperty(name)) {    setRequiredFields({ ...requiredFields, [name]: escapeAndTrim(value) })  }   \/\/ \u0435\u0441\u043b\u0438 \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u043e\u0441\u044c \u043b\u044e\u0431\u043e\u0435 \u043f\u043e\u043b\u0435  setFields({ ...fields, [name]: escapeAndTrim(value) }) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0444\u043e\u0440\u043c\u044b:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">const submit = async (e) => {  e.preventDefault()   \/\/ \u0437\u0430\u0449\u0438\u0442\u0430 \u043e\u0442 \u0434\u0443\u0440\u0430\u043a\u0430 \u043d\u0430 \u0441\u043b\u0443\u0447\u0430\u0439,  \/\/ \u0435\u0441\u043b\u0438 \u043a\u0442\u043e-\u0442\u043e \u0434\u043e\u0434\u0443\u043c\u0430\u0435\u0442\u0441\u044f \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043a\u043d\u043e\u043f\u043a\u0443 \u0434\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0444\u043e\u0440\u043c\u044b \u0447\u0435\u0440\u0435\u0437 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435  \/\/ \u044f \u043f\u043e\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u0440\u0430\u0437\u0434\u0435\u043b\u0435  if (disabled) return   \/\/ \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438  setLoading(true)   \/\/ \u0435\u0441\u043b\u0438 \u0438\u043c\u0435\u044e\u0442\u0441\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u044b  if (validators) {    \/\/ \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u043e\u043b\u044f \u0434\u043b\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438    const fieldsToValidate = requiredFields ? requiredFields : fields    \/\/ \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u043c \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043e\u0448\u0438\u0431\u043a\u0438    const validationErrors = validate(fieldsToValidate, validators, messages)    \/\/ \u0435\u0441\u043b\u0438 \u0438\u043c\u0435\u044e\u0442\u0441\u044f \u043e\u0448\u0438\u0431\u043a\u0438    if (Object.keys(validationErrors).length > 0) {      \/\/ \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438      setLoading(false)      \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u043e\u0448\u0438\u0431\u043a\u0430\u043c\u0438 \u0438 \u043f\u0440\u0435\u043a\u0440\u0430\u0449\u0430\u0435\u043c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0441\u043a\u0440\u0438\u043f\u0442\u0430      return setErrors(validationErrors)    }  }   \/\/ \u0435\u0441\u043b\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u0430 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f, \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u0435\u0435 \u0441 \u043f\u043e\u043b\u044f\u043c\u0438 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430  if (typeof onSubmit === 'function') {    await onSubmit(fields)  } else {    \/\/ \u0438\u043d\u0430\u0447\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440, \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043e\u0442\u0432\u0435\u0442 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0435\u0433\u043e    const response = await simpleFetch({       url,       method,       body: fields,       ...fetchOptions     })    setResponse(response)  }   \/\/ \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438  setLoading(false) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041d\u0430\u043a\u043e\u043d\u0435\u0446, \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0434\u043b\u044f \u0441\u0431\u0440\u043e\u0441\u0430 \u0444\u043e\u0440\u043c\u044b:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">const reset = () => {  \/\/ \u0441\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u0435\u0439  setFields(initialData)  \/\/ \u0441\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442 \u043e\u0442\u0432\u0435\u0442\u0430  setResponse(null)  \/\/ \u0441\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u043e\u0448\u0438\u0431\u043a\u0430\u043c\u0438  setErrors(initialData) }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u043e\u0436\u0430\u043b\u0443\u0439, \u044d\u0442\u043e \u0432\u0441\u0435, \u0447\u0442\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0444\u043e\u0440\u043c.<\/p>\n<p>  <\/p>\n<p>\u0423 \u0432\u0430\u0441 \u043c\u043e\u0436\u0435\u0442 \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u0443\u0442\u044c \u0434\u0432\u0430 \u0432\u043e\u043f\u0440\u043e\u0441\u0430:<\/p>\n<p>  <\/p>\n<ol>\n<li>\u0420\u0430\u0437\u0432\u0435 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0435\u0439 \u0438\u043d\u043f\u0443\u0442\u043e\u0432 \u0434\u043e\u043b\u0436\u0435\u043d \u0437\u0430\u043d\u0438\u043c\u0430\u0442\u044c\u0441\u044f \u043d\u0435 \u0441\u0435\u0440\u0432\u0435\u0440? \u0427\u0442\u043e \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442 JavaScript \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435? \u0421\u043c\u043e\u0436\u0435\u0442 \u043b\u0438 \u043e\u043d \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043f\u0443\u0441\u0442\u0443\u044e \u0444\u043e\u0440\u043c\u0443? \u041c\u044b \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0438 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u0447\u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f.<\/li>\n<li>\u041f\u043e\u0447\u0435\u043c\u0443 \u043c\u044b \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u043e\u0448\u0438\u0431\u043a\u0430\u043c\u0438 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u0438\u043d\u043f\u0443\u0442\u043e\u0432? \u0420\u0430\u0437\u0432\u0435 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c \u044d\u0442\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043d\u0435 <code>null<\/code> \u0438\u043b\u0438 \u0445\u043e\u0442\u044f \u0431\u044b \u043f\u0443\u0441\u0442\u043e\u0439 \u043e\u0431\u044a\u0435\u043a\u0442? \u041f\u043e \u0431\u043e\u043b\u044c\u0448\u043e\u043c\u0443 \u0441\u0447\u0435\u0442\u0443, \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c <code>null<\/code>, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043c\u044b \u0435\u0449\u0435 \u043d\u0435 \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u043b\u0438 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438. \u041e\u0434\u043d\u0430\u043a\u043e, \u043a\u0430\u043a \u043c\u044b \u0443\u0432\u0438\u0434\u0438\u043c \u0434\u0430\u043b\u0435\u0435, <code>null<\/code> \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043f\u0435\u0440\u0435\u0434 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0435\u0439 \u043e\u0448\u0438\u0431\u043a\u0438 \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0438 \u043a\u043e\u0433\u0434\u0430 \u0442\u0430\u043a\u0438\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 (\u0447\u0438\u0442\u0430\u0439 \u0438\u043d\u043f\u0443\u0442\u043e\u0432) \u043c\u043d\u043e\u0433\u043e, \u044d\u0442\u043e \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a \u043c\u043d\u043e\u0433\u043e\u0447\u0438\u0441\u043b\u0435\u043d\u043d\u043e\u043c\u0443 \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u043a\u043e\u0434\u0430. \u042d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u043a\u0430\u043a \u0440\u0430\u0437 \u0438 \u0440\u0435\u0448\u0430\u0435\u0442 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0441 \u043e\u0448\u0438\u0431\u043a\u0430\u043c\u0438 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u0438\u043d\u043f\u0443\u0442\u043e\u0432.<\/li>\n<\/ol>\n<p>  <\/p>\n<h2 id=\"podgotovka-proekta-i-realizaciya-servera\">\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u0430<\/h2>\n<p>  <\/p>\n<p>\u0424\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u043e\u0431\u0449\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430:<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">simple-form-react  - client  - server<\/code><\/pre>\n<p>  <\/p>\n<p>\u041d\u0430\u0445\u043e\u0434\u044f\u0441\u044c \u0432 \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438, \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442 \u0438 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u043e\u0431\u0449\u0443\u044e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c:<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">yarn init -y  yarn add concurrently<\/code><\/pre>\n<p>  <\/p>\n<ul>\n<li><a href=\"https:\/\/www.npmjs.com\/package\/concurrently\">concurrently<\/a> \u2014 \u044d\u0442\u043e \u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0433\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u043c\u0430\u043d\u0434, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0445 \u0432 <code>package.json<\/code><\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u0432 \u0432 package.json:<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">\"scripts\": {  \"dev\": \"concurrently \\\"yarn --cwd client start\\\" \\\"yarn --cwd server dev\\\"\" }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e <code>server<\/code>, \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442 \u0438 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438:<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">yarn init -y  yarn add bcrypt cors express jsonwebtoken nodemon<\/code><\/pre>\n<p>  <\/p>\n<ul>\n<li><a href=\"https:\/\/www.npmjs.com\/package\/bcrypt\">bcrypt<\/a> \u2014 \u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u0445\u0435\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0430\u0440\u043e\u043b\u0435\u0439<\/li>\n<li><a href=\"https:\/\/www.npmjs.com\/package\/cors\">cors<\/a> \u2014 \u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 HTTP-\u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u043e\u0432, \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445 \u0441 CORS (\u043e\u0431\u043c\u0435\u043d \u0440\u0435\u0441\u0443\u0440\u0441\u0430\u043c\u0438 \u043c\u0435\u0436\u0434\u0443 \u0440\u0430\u0437\u043d\u044b\u043c\u0438 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430\u043c\u0438)<\/li>\n<li><a href=\"https:\/\/expressjs.com\/ru\/\">express<\/a> \u2014 Node.js-\u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u0432<\/li>\n<li><a href=\"https:\/\/github.com\/auth0\/node-jsonwebtoken\">jsonwebtoken<\/a> \u2014 \u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 jwt-\u0442\u043e\u043a\u0435\u043d\u043e\u0432<\/li>\n<li><a href=\"https:\/\/www.npmjs.com\/package\/nodemon\">nodemon<\/a> \u2014 \u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u041a\u0430\u043a \u0432\u0438\u0434\u0438\u0442\u0435, \u0443 \u043d\u0430\u0441 \u0432\u0441\u0435 \u0441\u0435\u0440\u044c\u0435\u0437\u043d\u043e. \u041c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0444\u0438\u043a\u0442\u0438\u0432\u043d\u044b\u0439, \u043d\u043e \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0431\u043b\u0438\u0437\u043a\u0438\u0439 \u043a \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c\u0443 \u0441\u0435\u0440\u0432\u0438\u0441 \u0434\u043b\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.<\/p>\n<p>  <\/p>\n<p>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0432 <code>server\/package.json<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">\"dev\": \"nodemon index.js\", \"start\": \"node index.js\"<\/code><\/pre>\n<p>  <\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0432 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 <code>server<\/code> \u0444\u0430\u0439\u043b <code>index.js<\/code> \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u044f:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">const express = require('express') const cors = require('cors') const { randomBytes } = require('crypto') const bcrypt = require('bcrypt') const jwt = require('jsonwebtoken')  \/\/ \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f const app = express()  \/\/ \u0438\u0441\u043a\u0443\u0441\u0441\u0442\u0432\u0435\u043d\u043d\u0430\u044f \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0430 \u0434\u043b\u044f \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u0438 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430 const sleep = (ms) =>  new Promise((resolve) => {    const timerId = setTimeout(() => {      resolve()      clearTimeout(timerId)    }, ms)  })  \/\/ \u0440\u043e\u0443\u0442\u044b \u0434\u043b\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 const authRouter = express  .Router()  \/\/ \u0440\u043e\u0443\u0442 \u0434\u043b\u044f \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438  .post('\/register', async (req, res, next) => {    \/\/ \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0443    await sleep(1000)     \/\/ \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u0438\u0437 \u0442\u0435\u043b\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0430\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b \u0438 \u043f\u0430\u0440\u043e\u043b\u044c    const { email, password } = req.body     try {      \/\/ \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0438\u043c, \u0447\u0442\u043e \u0432 \u043d\u0430\u0448\u0435\u0439 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0443\u0436\u0435 \u0438\u043c\u0435\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0441 \u0430\u0434\u0440\u0435\u0441\u043e\u043c \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b `test@mail.com`      \/\/ \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0443\u043a\u0430\u0437\u0430\u043b \u0442\u0430\u043a\u043e\u0439 email, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043e\u0448\u0438\u0431\u043a\u0443 409 - \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0443\u0436\u0435 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d      if (email === 'test@mail.com') {        return res.sendStatus(409)      }       \/\/ \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f      const userId = randomBytes(16).toString('hex')       \/\/ \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u0442\u043e\u043a\u0435\u043d      const token = jwt.sign({ email }, 'secret', {        expiresIn: '1h'      })       \/\/ \u0445\u0435\u0448\u0438\u0440\u0443\u0435\u043c \u043f\u0430\u0440\u043e\u043b\u044c      const hashedPassword = await bcrypt.hash(password, 10)       \/\/ \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f      const user = {        userId,        email,        hashedPassword,        token,        createdAt: new Date().toISOString()      }       \/\/ \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0435\u0433\u043e      res.status(201).json(user)    } catch (err) {      next(err)    }  })  \/\/ \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f  .post('\/login', async (req, res, next) => {    \/\/ \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u0430\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b \u0438 \u043f\u0430\u0440\u043e\u043b\u044c \u0438\u0437 \u0442\u0435\u043b\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430    const { email, password } = req.body     try {      \/\/ \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0443      await sleep(1000)       \/\/ \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f      \/\/ throw new Error('error')       \/\/ email \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0434\u043e\u043b\u0436\u0435\u043d \u0438\u043c\u0435\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 `test@mail.com`      \/\/ \u0435\u0441\u043b\u0438 \u044d\u0442\u043e \u043d\u0435 \u0442\u0430\u043a, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043e\u0448\u0438\u0431\u043a\u0443 404 - \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d      if (email !== 'test@mail.com') {        return res.sendStatus(404)      }       const testPassword = await bcrypt.hash('test', 10)      const correctPassword = await bcrypt.compare(password, testPassword)      \/\/ \u043f\u0430\u0440\u043e\u043b\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0434\u043e\u043b\u0436\u0435\u043d \u0438\u043c\u0435\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 'test'      \/\/ \u0435\u0441\u043b\u0438 \u044d\u0442\u043e \u043d\u0435 \u0442\u0430\u043a, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043e\u0448\u0438\u0431\u043a\u0443 403      if (!correctPassword) {        return res.sendStatus(403)      }       \/\/ \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u0442\u043e\u043a\u0435\u043d      const token = jwt.sign({ email }, 'secret', {        expiresIn: '1h'      })       \/\/ \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0435\u0433\u043e      res.status(200).json({ token })    } catch (err) {      next(err)    }  })  \/\/ \u043e\u0442\u043a\u043b\u044e\u0447\u0430\u0435\u043c CORS app.use(cors()) \/\/ \u043f\u0430\u0440\u0441\u0438\u043c \u0442\u0435\u043b\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430 app.use(express.json())  \/\/ \u0440\u0435\u0448\u0430\u0435\u043c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u0441 \u0444\u0430\u0432\u0438\u043a\u043e\u043d\u043a\u043e\u0439 app.get('\/favicon.ico', (_, res) => {  res.sendStatus(200) }) \/\/ \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0440\u043e\u0443\u0442\u044b \/\/ \u043a\u043e\u043d\u0435\u0447\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u0434\u043b\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0431\u0443\u0434\u0435\u0442 \u0438\u043c\u0435\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 `http:\/\/localhost:5000\/api\/auth` app.use('\/api\/auth', authRouter)  \/\/ \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043e\u0448\u0438\u0431\u043e\u043a app.use(errorHandler)  \/\/ \u0430 \u0432\u043e\u0442 \u0438 \u043e\u043d function errorHandler(err, req, res, next) {  console.error(err)  res.sendStatus(500) }  const PORT = process.env.PORT || 5000 app.listen(PORT, () => {  console.log(` Server started on http:\/\/localhost:${PORT}`) })<\/code><\/pre>\n<p>  <\/p>\n<h2 id=\"realizaciya-frontenda\">\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u0430<\/h2>\n<p>  <\/p>\n<p>\u041d\u0430\u0445\u043e\u0434\u044f\u0441\u044c \u0432 \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438, \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0448\u0430\u0431\u043b\u043e\u043d React-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e [<code>create-react-app<\/code>]() \u0438 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c 2 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438:<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">yarn create react-app client  yarn add react-router-dom simple-form-react<\/code><\/pre>\n<p>  <\/p>\n<ul>\n<li><a href=\"https:\/\/reactrouter.com\/web\/guides\/quick-start\">react-router-dom<\/a> \u2014 \u043d\u0430\u0431\u043e\u0440 \u0443\u0442\u0438\u043b\u0438\u0442 \u0434\u043b\u044f \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0446\u0438\u0438<\/li>\n<li><a href=\"https:\/\/www.npmjs.com\/package\/simple-form-react\">simple-form-react<\/a> \u2014 \u043d\u0430\u0448 \u0445\u0443\u043a<\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043d\u0430\u0448\u0435\u0433\u043e React-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0442\u0430\u043a\u043e\u0439:<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">client  - public    - index.html  - src    - components      - Form        - Button.js - \u043a\u043d\u043e\u043f\u043a\u0430        - Error.js - \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430        - Field.js - \u043f\u043e\u043b\u0435        - index.js - \u0444\u043e\u0440\u043c\u0430        - Result.js - \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442        - validators.js - \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u044b \u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f      - index.js - \u0430\u0433\u0440\u0435\u0433\u0430\u0442\u043e\u0440 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432      - Loader.js - \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438      - Login.js - \u0444\u043e\u0440\u043c\u0430 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438      - Register.js - \u0444\u043e\u0440\u043c\u0430 \u0434\u043b\u044f \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438    - pages      - Auth.js - \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438\/\u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438      - Home.js - \u0434\u043e\u043c\u0430\u0448\u043d\u044f\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430      - index.js - \u0430\u0433\u0440\u0435\u0433\u0430\u0442\u043e\u0440 \u0441\u0442\u0440\u0430\u043d\u0438\u0446    - App.js - \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442    - index.css - \u0441\u0442\u0438\u043b\u0438    - index.js - \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0444\u0430\u0439\u043b  - jsconfig.json<\/code><\/pre>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0441\u0442\u0438\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/getbootstrap.com\/\"><code>Bootstrap<\/code><\/a>. \u041d\u0430\u043c \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u044e\u0442\u0441\u044f \u0438\u043a\u043e\u043d\u043a\u0438 \u0438\u0437 \u0434\u0430\u043d\u043d\u043e\u0433\u043e CSS-\u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 \u0438 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u0448\u0440\u0438\u0444\u0442. \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0438\u0445 \u0432 <code>public\/index.html<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"html\">&lt;link  href=\"https:\/\/fonts.googleapis.com\/css2?family=Montserrat&amp;display=swap\"  rel=\"stylesheet\" \/> &lt;link  href=\"https:\/\/cdn.jsdelivr.net\/npm\/bootstrap@5.0.1\/dist\/css\/bootstrap.min.css\"  rel=\"stylesheet\"  integrity=\"sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x\"  crossorigin=\"anonymous\" \/> &lt;link  rel=\"stylesheet\"  href=\"https:\/\/cdn.jsdelivr.net\/npm\/bootstrap-icons@1.5.0\/font\/bootstrap-icons.css\" \/><\/code><\/pre>\n<p>  <\/p>\n<p>\u041d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u043e\u043f\u0440\u0430\u0432\u0438\u043c \u0441\u0442\u0438\u043b\u0438 \u0432 <code>src\/index.css<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"css\">* {  font-family: 'Montserrat', sans-serif;  text-align: center; }  h1, h2, h3 {  margin: 1rem 0; }  .field {  margin: 1rem 0;  text-align: left; }  input {  text-align: left; }  p {  margin: 0.5rem 0;  word-break: break-all; }  i {  margin-right: 0.5rem; }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u0440\u043e\u043a\u0441\u0438\u0440\u0443\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0432 <code>package.json<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">\"proxy\": \"http:\/\/localhost:5000\"<\/code><\/pre>\n<p>  <\/p>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u0444\u0430\u0439\u043b\u043e\u0432.<\/p>\n<p>  <\/p>\n<p><code>index.js<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import React, { StrictMode } from 'react' import { render } from 'react-dom' import '.\/index.css' import { App } from '.\/App'  render(  &lt;StrictMode>    &lt;App \/>  &lt;\/StrictMode>,  document.getElementById('root') )<\/code><\/pre>\n<p>  <\/p>\n<p>\u0422\u0443\u0442 \u0432\u0441\u0435 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e.<\/p>\n<p>  <\/p>\n<p>\u0412 <code>App.js<\/code> \u043c\u044b \u0434\u0435\u043b\u0430\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u043d\u0443\u0436\u043d\u044b\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0438\u0437 <code>react-router-dom<\/code><\/li>\n<li>\u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0438\u0437 <code>pages<\/code><\/li>\n<li>\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0440\u043e\u0443\u0442\u044b<\/li>\n<li>\u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442<\/li>\n<\/ul>\n<p>  <\/p>\n<pre><code class=\"javascript\">import {  BrowserRouter as Router,  NavLink,  Route,  Switch } from 'react-router-dom'  import { Home, Auth } from 'pages'  const routes = [  {    name: 'Home',    path: '\/',    exact: true,    component: Home  },  {    name: 'Auth',    path: '\/auth',    component: Auth  } ]  export const App = () => (  &lt;div className='container d-flex flex-column align-items-center'>    &lt;Router>      &lt;nav className='navbar navbar-light bg-light'>        &lt;ul className='nav'>          {routes.map(({ name, path }) => (            &lt;li className='nav-item' key={name}>              &lt;NavLink to={path} className='nav-link' activeClassName='active'>                {name}              &lt;\/NavLink>            &lt;\/li>          ))}        &lt;\/ul>      &lt;\/nav>      &lt;h1>Simple Form React&lt;\/h1>      &lt;Switch>        {routes.map(({ name, ...rest }) => (          &lt;Route key={name} {...rest} \/>        ))}      &lt;\/Switch>    &lt;\/Router>  &lt;\/div> )<\/code><\/pre>\n<p>  <\/p>\n<p>\u0422\u043e\u0436\u0435 \u043d\u0438\u0447\u0435\u0433\u043e \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0433\u043e.<\/p>\n<p>  <\/p>\n<p><code>pages\/Home.js<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">export const Home = () => &lt;h2>Home Page&lt;\/h2><\/code><\/pre>\n<p>  <\/p>\n<p>\u0411\u0435\u0437 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0435\u0432.<\/p>\n<p>  <\/p>\n<p><code>pages\/Auth.js<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import { useState } from 'react' \/\/ \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b import { Login, Register } from 'components'  export const Auth = () => {  \/\/ \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043e\u0434\u043d\u0443 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0438 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u0434\u043b\u044f \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438  \/\/ \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0443\u0441\u043b\u043e\u0432\u043d\u043e\u0433\u043e \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433\u0430  const [login, setLogin] = useState(true)   return (    &lt;>      &lt;h2>Auth Page&lt;\/h2>      {login ? &lt;Login \/> : &lt;Register \/>}      {\/* \u043a\u043d\u043e\u043f\u043a\u0430 \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043c\u0435\u0436\u0434\u0443 \u0444\u043e\u0440\u043c\u0430\u043c\u0438 *\/}      &lt;button className='btn btn-link mt-2' onClick={() => setLogin(!login)}>        {login ? 'Register' : 'Login'}      &lt;\/button>    &lt;\/>  ) }<\/code><\/pre>\n<p>  <\/p>\n<p><code>components\/Loader.js<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">export const Loader = () => (  &lt;div className='spinner-border text-primary' role='status'>    &lt;span className='visually-hidden'>Loading...&lt;\/span>  &lt;\/div> )<\/code><\/pre>\n<p>  <\/p>\n<p>\u0412 <code>components\/Login.js<\/code> \u043c\u044b \u0434\u0435\u043b\u0430\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0444\u043e\u0440\u043c\u044b<\/li>\n<li>\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u043e\u043b\u044f \u0444\u043e\u0440\u043c\u044b<\/li>\n<li>\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u043d\u043f\u0443\u0442\u043e\u0432<\/li>\n<li>\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u0440\u043e\u043f\u044b \u0434\u043b\u044f \u0444\u043e\u0440\u043c\u044b<\/li>\n<li>\u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442<\/li>\n<\/ul>\n<p>  <\/p>\n<pre><code class=\"javascript\">import { Form } from 'components'  const inputs = [  {    label: 'Email',    name: 'email',    type: 'email'  },  {    label: 'Password',    name: 'password',    type: 'password'  } ]  const initialData = {  email: '',  password: '' }  const formProps = {  initialData,  url: '\/login',  inputs,  submitLabel: 'Login' }  export const Login = () => (  &lt;>    &lt;h3>Login&lt;\/h3>    &lt;Form {...formProps} \/>  &lt;\/> )<\/code><\/pre>\n<p>  <\/p>\n<p>\u0412 <code>components\/Register.js<\/code> \u043c\u044b \u0434\u0435\u043b\u0430\u0435\u043c \u043f\u043e\u0447\u0442\u0438 \u0442\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435, \u0437\u0430 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u043d\u0435 \u0432\u0441\u0435 \u043f\u043e\u043b\u044f \u0444\u043e\u0440\u043c\u044b \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438; \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0438\u043d\u043f\u0443\u0442\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0438\u043c\u0435\u0442\u044c \u0430\u0442\u0440\u0438\u0431\u0443\u0442 <code>required<\/code><\/li>\n<li>\u043c\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u044b \u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0430\u0445<\/li>\n<li>\u043f\u0440\u043e\u043f\u044b \u0434\u043b\u044f \u0444\u043e\u0440\u043c\u044b \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0442 \u043f\u043e\u043b\u0435 <code>required<\/code> \u0441\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c <code>false<\/code> \u0434\u043b\u044f \u0445\u0443\u043a\u0430<\/li>\n<li>\u043c\u044b \u043d\u0435 \u043b\u0438\u0448\u0430\u0435\u043c \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e \u0438\u043d\u043f\u0443\u0442\u043e\u0432 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0445 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432 \u0442\u0438\u043f\u0430 <code>minlength<\/code>, <code>min<\/code>, <code>max<\/code> \u0438 \u0442.\u0434., \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0442\u043e\u0442 \u0430\u0442\u0440\u0438\u0431\u0443\u0442 <code>required<\/code><\/li>\n<\/ul>\n<p>  <\/p>\n<pre><code class=\"javascript\">import { Form } from 'components'  const inputs = [  \/\/ \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u0435  {    label: 'Name',    name: 'name',    type: 'text',    minlength: 2  },  \/\/ \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u0435  {    label: 'Age',    name: 'age',    type: 'number',    min: 18,    max: 65,    step: 1  },  \/\/ \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u043b\u044f  {    label: 'Email',    name: 'email',    type: 'email',    required: true  },  {    label: 'Password',    name: 'password',    type: 'password',    required: true  },  {    label: 'Confirm password',    name: 'confirmPassword',    type: 'password',    required: true  } ]  \/\/ \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u044b \/\/ \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u043b\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u0430 const validators = {  \/\/ \u0438\u043c\u044f \u0434\u043e\u043b\u0436\u043d\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u0442\u044c \u043a\u0430\u043a \u043c\u0438\u043d\u0438\u043c\u0443\u043c \u0438\u0437 2 \u0441\u0438\u043c\u0432\u043e\u043b\u043e\u0432  name: (value) => value.length > 1,  \/\/ \u0432\u043e\u0437\u0440\u0430\u0441\u0442 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0432 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0435 18-65  age: (value) => value > 17 &amp;&amp; value &lt; 66 }  \/\/ \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f const messages = {  name: 'Your name is too short!',  age: `You're too young or too old!` }  const initialData = {  name: '',  age: '',  email: '',  password: '',  confirmPassword: '' }  const formProps = {  initialData,  url: '\/register',  inputs,  required: false,  validators,  messages,  submitLabel: 'Register' }  export const Register = () => (  &lt;>    &lt;h3>Register&lt;\/h3>    &lt;Form {...formProps} \/>  &lt;\/> )<\/code><\/pre>\n<p>  <\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043a \u0441\u0430\u043c\u043e\u043c\u0443 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u043c\u0443 \u2014 \u0444\u043e\u0440\u043c\u0435.<\/p>\n<p>  <\/p>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u043e\u0432 \u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0430\u0445 (<code>Form\/validators.js<\/code>):<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">\/\/ \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440 const isEmail = (email) => \/\\S+@\\S+\\.\\S+\/.test(email)  \/\/ \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u0434\u0435\u0444\u043e\u043b\u0442\u043d\u044b\u043c \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u043e\u043c export const defaultValidators = {  email: isEmail }  \/\/ \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0430\u0445, \u0432\u043e\u0437\u043d\u0438\u043a\u0448\u0438\u0445 \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0430, - \u043e\u0448\u0438\u0431\u043a\u0430\u0445 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0438\u043d\u043f\u0443\u0442\u043e\u0432 \/\/ \u043a\u043b\u044e\u0447\u0430\u043c\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u043f\u043e\u043b\u0435\u0439 export const errorMessagesClient = {  email: 'Wrong email!',  confirmPassword: 'Passwords must be the same!' }  \/\/ \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0430\u0445, \u0432\u043e\u0437\u043d\u0438\u043a\u0448\u0438\u0445 \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \/\/ \u043a\u043b\u044e\u0447\u0430\u043c\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0441\u0442\u0430\u0442\u0443\u0441-\u043a\u043e\u0434\u044b export const errorMessagesServer = {  404: 'User not found!',  403: 'Wrong credentials!',  409: 'Email already in use!',  500: 'Something went wrong. Try again later' } <\/code><\/pre>\n<p>  <\/p>\n<p><code>Form\/Button.js<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">const buttons = ((b) => ({  success: `${b}-success`,  warning: `${b}-warning` }))('btn btn')  export const Button = ({ label, variant, ...rest }) => (  &lt;button className={buttons[variant]} {...rest}>    {label}  &lt;\/button> )<\/code><\/pre>\n<p>  <\/p>\n<p><em>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/em> \u043d\u0430 \u0442\u043e, \u043a\u0430\u043a \u043c\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043a\u043b\u0430\u0441\u0441\u044b \u0434\u043b\u044f \u043a\u043d\u043e\u043f\u043e\u043a. \u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0441\u0430\u043c\u043e\u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e (IIFE), \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0449\u0443\u044e \u043e\u0431\u044a\u0435\u043a\u0442, \u043a\u043b\u044e\u0447\u0430\u043c\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u043a\u043d\u043e\u043f\u043a\u0438. \u0422\u0430\u043a\u0443\u044e \u0436\u0435 \u0442\u0435\u0445\u043d\u0438\u043a\u0443 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u043a\u043b\u0430\u0441\u0441\u043e\u0432 \u0438\u043a\u043e\u043d\u043e\u043a.<\/p>\n<p>  <\/p>\n<p><code>Form\/Error.js<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">import { errorMessagesServer } from '.\/validators'  export const Error = ({ status }) => (  &lt;div className='text-danger'>    &lt;p>{errorMessagesServer[status]}&lt;\/p>  &lt;\/div> )<\/code><\/pre>\n<p>  <\/p>\n<p><em>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/em> \u043d\u0430 \u0442\u043e, \u043a\u0430\u043a \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u043c\u0443 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044e \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435.<\/p>\n<p>  <\/p>\n<p><code>Form\/Result.js<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">\/\/ \u043f\u0440\u043e\u043f `result` - \u044d\u0442\u043e \u043e\u0431\u044a\u0435\u043a\u0442 export const Result = ({ result }) => (  &lt;>    &lt;h4>Result&lt;\/h4>    &lt;div>      {Object.entries(result).map(([key, value], index) => (        &lt;p key={index}>          {key}: {value}        &lt;\/p>      ))}    &lt;\/div>  &lt;\/> )<\/code><\/pre>\n<p>  <\/p>\n<p><code>Form\/Field.js<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"javascript\">const icons = ((i) => ({  name: `${i}-person`,  age: `${i}-person-fill`,  email: `${i}-envelope`,  password: `${i}-key`,  confirmPassword: `${i}-key-fill` }))('bi bi')  export const Field = ({ label, name, error, ...rest }) => (  &lt;div className='field'>    &lt;label htmlFor={name} className='form-label'>      &lt;i className={icons[name]}>&lt;\/i>      &lt;span>{label}&lt;\/span>    &lt;\/label>    &lt;input name={name} id={name} className='form-control' {...rest} \/>    {\/* \u043e\u0448\u0438\u0431\u043a\u0430 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 *\/}    &lt;p className='text-danger'>{error}&lt;\/p>  &lt;\/div> )<\/code><\/pre>\n<p>  <\/p>\n<p>\u0412 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0435 \u0444\u043e\u0440\u043c\u044b (<code>Form\/index.js<\/code>) \u043c\u044b \u0434\u0435\u043b\u0430\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<p>  <\/p>\n<ul>\n<li>\u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0444\u043e\u0440\u043c\u044b \u0438 \u043b\u043e\u0430\u0434\u0435\u0440<\/li>\n<li>\u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u0434\u0435\u0444\u043e\u043b\u0442\u043d\u044b\u0435 \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u044b \u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043e \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u0445 \u043e\u0448\u0438\u0431\u043a\u0430\u0445<\/li>\n<li>\u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u043d\u0430\u0448 \u0445\u0443\u043a \u0438 <code>simpleFetch<\/code><\/li>\n<li>\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0430\u0434\u0440\u0435\u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u0435\u0442\u0442\u0435\u0440\u0430 <code>baseUrl<\/code><\/li>\n<li>\u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u0434\u043e\u0447\u0435\u0440\u043d\u0438\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b, \u0438\u043d\u043f\u0443\u0442\u044b, \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u044b, \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f, \u043f\u043e\u0434\u043f\u0438\u0441\u044c \u0434\u043b\u044f \u043a\u043d\u043e\u043f\u043a\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0444\u043e\u0440\u043c\u044b \u0438 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0435 \u0438\u0437 \u043f\u0440\u043e\u043f\u043e\u0432<\/li>\n<li>\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u0440\u043e\u043f\u044b \u0434\u043b\u044f \u0445\u0443\u043a\u0430<\/li>\n<li>\u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u0445\u0443\u043a \u0438 \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u0432\u0441\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0435 \u0438\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f<\/li>\n<li>\u043f\u0440\u0438 <code>loading: true<\/code> \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043b\u043e\u0430\u0434\u0435\u0440<\/li>\n<li>\u043f\u0440\u0438 \u043d\u0430\u043b\u0438\u0447\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442<\/li>\n<li>\u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043c \u0444\u043e\u0440\u043c\u0443<\/li>\n<\/ul>\n<p>  <\/p>\n<pre><code class=\"javascript\">\/\/ import { useState } from 'react' import { Loader } from 'components'  import { Field } from '.\/Field' import { Button } from '.\/Button' import { Result } from '.\/Result' import { Error } from '.\/Error'  import { defaultValidators, errorMessagesClient } from '.\/validators'  import useSimpleForm, { simpleFetch } from 'useSimpleForm' simpleFetch.baseUrl = 'http:\/\/localhost:5000\/api\/auth'  export const Form = (props) => {  \/\/ const [data, setData] = useState(null)   const { children, inputs, validators, messages, submitLabel, ...rest } = props   const hookProps = {    ...rest,    validators: { ...defaultValidators, ...validators },    messages: { ...errorMessagesClient, ...messages }  }   \/*  hookProps.fetchOptions = {      handlers: {        onSuccess: (response) => {          setData(response.data)        }      }    }  *\/   \/*  hookProps.onSubmit = async (fields) => {    const response = await simpleFetch.post(rest.url, fields)    setData(response.data)  }  *\/   const { fields, change, submit, reset, disabled, loading, response, errors } =    useSimpleForm(hookProps)   if (loading) return &lt;Loader \/>  if (response?.data) return &lt;Result result={response.data} \/>  \/\/ if (data) return &lt;Result result={data} \/>   return (    &lt;form onSubmit={submit} onReset={reset}>      {inputs.map((input, index) => (        &lt;Field          key={index}          value={fields[input.name]}          onChange={change}          error={errors[input.name]}          {...input}        \/>      ))}      {children}      {response?.error &amp;&amp; &lt;Error status={response.info.status} \/>}      &lt;div>        &lt;Button label={submitLabel} variant='success' disabled={disabled} \/>        &lt;Button label='Reset' type='reset' variant='warning' \/>      &lt;\/div>    &lt;\/form>  ) }<\/code><\/pre>\n<p>  <\/p>\n<p><em>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/em> \u043d\u0430 \u0437\u0430\u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u044b: \u0432 \u043d\u0438\u0445 \u043f\u0440\u0438\u0432\u043e\u0434\u044f\u0442\u0441\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u0434\u043b\u044f <code>simpleFetch<\/code> \u0438 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0434\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0444\u043e\u0440\u043c\u044b. \u0412 \u043e\u0431\u043e\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u0435\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u044c \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0445 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c. \u0422\u0430\u043a\u0436\u0435 <em>\u043e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/em> \u043d\u0430 \u0442\u043e, \u043a\u0430\u043a \u043c\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u043e\u0448\u0438\u0431\u043a\u0438 \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430. \u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <a href=\"https:\/\/developer.mozilla.org\/ru\/docs\/Web\/JavaScript\/Reference\/Operators\/Optional_chaining\">\u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440 \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0439 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438<\/a> \u0434\u043b\u044f \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0443 \u043f\u043e\u0442\u0435\u043d\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u043d\u0435\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0432\u043c\u0435\u0441\u0442\u043e <code>response &amp;&amp; response.data<\/code>, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440.<\/p>\n<p>  <\/p>\n<h2 id=\"proverka-rabotosposobnosti\">\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0440\u0430\u0431\u043e\u0442\u043e\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u0438<\/h2>\n<p>  <\/p>\n<p>\u041f\u0440\u0438\u0448\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u043e\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0448\u0435\u0433\u043e \u0445\u0443\u043a\u0430 \u0438 \u0444\u043e\u0440\u043c\u044b.<\/p>\n<p>  <\/p>\n<p>\u041d\u0430\u0445\u043e\u0434\u044f\u0441\u044c \u0432 \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438, \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 <code>yarn dev<\/code> \u0438\u043b\u0438 <code>npm run dev<\/code>.<\/p>\n<p>  <\/p>\n<p>\u042d\u0442\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u044b \u0438 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0432\u043a\u043b\u0430\u0434\u043a\u0443 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430 \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 <code>http:\/\/localhost:3000<\/code>:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/j8\/tc\/ew\/j8tcewkiprihqq7ktlb2pfxu_a0.png\" data-src=\"https:\/\/habrastorage.org\/webt\/j8\/tc\/ew\/j8tcewkiprihqq7ktlb2pfxu_a0.png\"\/>  <\/p>\n<p>  \u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/ox\/zh\/3h\/oxzh3h79i3ttnjucygdwbbxxesw.png\" data-src=\"https:\/\/habrastorage.org\/webt\/ox\/zh\/3h\/oxzh3h79i3ttnjucygdwbbxxesw.png\"\/>  <\/p>\n<p>  \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043f\u0443\u0441\u0442\u0443\u044e \u0444\u043e\u0440\u043c\u0443, \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0432 <code>JavaScript<\/code>. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0432 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u0445 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u043c <code>Cmd + Shift + P<\/code>, \u0432\u0432\u043e\u0434\u0438\u043c <code>j<\/code> \u0438 \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u043c <code>Debugger Disable JavaScript<\/code>:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/mh\/5x\/x2\/mh5xx2unohsfzdmmp0juuphtaqa.png\" data-src=\"https:\/\/habrastorage.org\/webt\/mh\/5x\/x2\/mh5xx2unohsfzdmmp0juuphtaqa.png\"\/>  <\/p>\n<p>  \u0412\u0438\u0434\u0438\u043c, \u0447\u0442\u043e \u043a\u043d\u043e\u043f\u043a\u0430 \u0434\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0444\u043e\u0440\u043c\u044b \u043e\u0441\u0442\u0430\u043b\u0430\u0441\u044c \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439. \u041d\u0430\u0445\u043e\u0434\u0438\u043c \u0435\u0435 \u0432 \u0440\u0430\u0437\u0434\u0435\u043b\u0435 <code>Elements<\/code> \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u0438 \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u0443 \u043d\u0435\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442 <code>disabled<\/code>:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/zb\/6q\/uj\/zb6qujckkcs_iayuzlu_rzbxltc.png\" data-src=\"https:\/\/habrastorage.org\/webt\/zb\/6q\/uj\/zb6qujckkcs_iayuzlu_rzbxltc.png\"\/>  <\/p>\n<p>  \u0412\u0438\u0434\u0438\u043c, \u0447\u0442\u043e \u043a\u043d\u043e\u043f\u043a\u0430 \u0441\u0442\u0430\u043b\u0430 \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0439. \u041d\u0430\u0436\u0438\u043c\u0430\u0435\u043c \u0435\u0435 \u0438\u2026 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 <code>You need to enable JavaScript to run this app.<\/code>:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/dp\/hy\/oo\/dphyoo6ckylwqlnabhy9pzde6f4.png\" data-src=\"https:\/\/habrastorage.org\/webt\/dp\/hy\/oo\/dphyoo6ckylwqlnabhy9pzde6f4.png\"\/>  <\/p>\n<p>  \u0410 \u0447\u0442\u043e \u0435\u0441\u043b\u0438 \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0430\u0442\u0440\u0438\u0431\u0443\u0442 <code>disabled<\/code> \u0441 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044b\u043c <code>JavaScript<\/code>? \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0432 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f \u043e\u0431\u043e\u0439\u0442\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u0443.<\/p>\n<p>  <\/p>\n<p>\u0412\u043a\u043b\u044e\u0447\u0430\u0435\u043c <code>JavaScript<\/code>, \u0443\u0434\u0430\u043b\u044f\u0435\u043c <code>disabled<\/code>, \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u043c \u043a\u043d\u043e\u043f\u043a\u0443 \u0438\u2026 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0437\u0430\u0449\u0438\u0442\u0430 \u043e\u0442 \u0434\u0443\u0440\u0430\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b\u0438 \u0432 \u043d\u0430\u0448\u0435\u043c \u0445\u0443\u043a\u0435 \u2014 <code>if (disabled) return<\/code>.<\/p>\n<p>  <\/p>\n<p>\u0412\u0432\u043e\u0434\u0438\u043c \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u044b\u0439 email \u0438 \u043a\u0430\u043a\u043e\u0439-\u043d\u0438\u0431\u0443\u0434\u044c \u043f\u0430\u0440\u043e\u043b\u044c (\u043a\u0440\u043e\u043c\u0435 <code>test<\/code>), \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u043c <code>Enter<\/code> \u0438\u043b\u0438 \u043a\u043d\u043e\u043f\u043a\u0443 <code>Login<\/code>:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/ik\/tf\/-g\/iktf-geaqwut1y3jqo03wyn5exc.png\" data-src=\"https:\/\/habrastorage.org\/webt\/ik\/tf\/-g\/iktf-geaqwut1y3jqo03wyn5exc.png\"\/>  <\/p>\n<p>  \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043e\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 (\u0445\u0443\u043a\u0430) \u043e \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u043c email.<\/p>\n<p>  <\/p>\n<p>\u0412\u0432\u043e\u0434\u0438\u043c <code>test@mail.com<\/code>:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/5r\/ye\/4n\/5rye4nbeuy9ehh5yleeozcx--pm.png\" data-src=\"https:\/\/habrastorage.org\/webt\/5r\/ye\/4n\/5rye4nbeuy9ehh5yleeozcx--pm.png\"\/>  <\/p>\n<p>  \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043e \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.<\/p>\n<p>  <\/p>\n<p>\u0412\u0432\u043e\u0434\u0438\u043c <code>test<\/code> \u0432 \u043f\u043e\u043b\u0435 \u0434\u043b\u044f \u043f\u0430\u0440\u043e\u043b\u044f:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/cc\/c6\/a8\/ccc6a8czfoqemnrajt-thzmdmc0.png\" data-src=\"https:\/\/habrastorage.org\/webt\/cc\/c6\/a8\/ccc6a8czfoqemnrajt-thzmdmc0.png\"\/>  <\/p>\n<p>  \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0441 \u0442\u043e\u043a\u0435\u043d\u043e\u043c.<\/p>\n<p>  <\/p>\n<p>\u041f\u0430\u0440\u043e\u0447\u043a\u0430 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u043e\u0432 \u0441 \u0444\u043e\u0440\u043c\u043e\u0439 \u0434\u043b\u044f \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438.<\/p>\n<p>  <\/p>\n<p>\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044f \u0434\u043b\u044f \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u043e\u043b\u044f \u043d\u0435 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0435\u0442 \u0441\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c \u043f\u043e\u043b\u044f \u0434\u043b\u044f \u043f\u0430\u0440\u043e\u043b\u044f:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/_b\/j2\/oo\/_bj2ooqb11hchvrfprfdo40dyoo.png\" data-src=\"https:\/\/habrastorage.org\/webt\/_b\/j2\/oo\/_bj2ooqb11hchvrfprfdo40dyoo.png\"\/>  <\/p>\n<p>  \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0441 \u0442\u0430\u043a\u0438\u043c email \u0443\u0436\u0435 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/it\/aq\/rk\/itaqrkilen-_rgyuwht6ubok3xa.png\" data-src=\"https:\/\/habrastorage.org\/webt\/it\/aq\/rk\/itaqrkilen-_rgyuwht6ubok3xa.png\"\/>  <\/p>\n<p>  \u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/bi\/mw\/nt\/bimwnt09rpyyosdfokgvb3e-skk.png\" data-src=\"https:\/\/habrastorage.org\/webt\/bi\/mw\/nt\/bimwnt09rpyyosdfokgvb3e-skk.png\"\/>  <\/p>\n<h2 id=\"zaklyuchenie\">\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h2>\n<p>  <\/p>\n<p>\u041d\u0435 \u043b\u044e\u0431\u043b\u044e \u043f\u0438\u0441\u0430\u0442\u044c \u0437\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0435 \u0431\u0443\u0434\u0443.<\/p>\n<p>  <\/p>\n<p>\u041d\u0430\u0434\u0435\u044e\u0441\u044c, \u0447\u0442\u043e \u0432\u044b \u043d\u0435 \u0437\u0440\u044f \u043f\u043e\u0442\u0440\u0430\u0442\u0438\u043b\u0438 \u0432\u0440\u0435\u043c\u044f \u0438 \u043d\u0430\u0448\u043b\u0438 \u0434\u043b\u044f \u0441\u0435\u0431\u044f \u0447\u0442\u043e-\u0442\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0435.<\/p>\n<p>  <\/p>\n<p>\u041b\u044e\u0431\u0430\u044f \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u0438\u0432\u043d\u0430\u044f \u043a\u0440\u0438\u0442\u0438\u043a\u0430 \u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442\u0441\u044f. \u041b\u044e\u0431\u044b\u0435 \u0437\u0430\u043c\u0435\u0447\u0430\u043d\u0438\u044f \u0438 \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043f\u043e \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044e \u0445\u0443\u043a\u0430 \u0432 \u0444\u043e\u0440\u043c\u0435 \u043b\u0438\u0447\u043d\u044b\u0445 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0438\u043b\u0438 \u043f\u0443\u043b-\u0440\u0435\u043a\u0432\u0435\u0441\u0442\u043e\u0432 \u043d\u0435\u043f\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0431\u0443\u0434\u0443\u0442 \u0443\u0447\u0442\u0435\u043d\u044b \u0432 \u0431\u0443\u0434\u0443\u0449\u0438\u0445 \u0432\u0435\u0440\u0441\u0438\u044f\u0445 \u043f\u0430\u043a\u0435\u0442\u0430.<\/p>\n<p>  <\/p>\n<p>\u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044e \u0437\u0430 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u0438 \u0445\u043e\u0440\u043e\u0448\u0435\u0433\u043e \u0434\u043d\u044f!<\/p>\n<p>  <\/p>\n<hr\/>\n<p>  <\/p>\n<p><a href=\"https:\/\/timeweb.com\/ru\/?utm_source=habr&amp;utm_medium=banner&amp;utm_campaign=10S&amp;utm_content=low\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/wo\/qf\/6c\/woqf6cweprtwtuq2m7jch92h5wk.png\" data-src=\"https:\/\/habrastorage.org\/webt\/wo\/qf\/6c\/woqf6cweprtwtuq2m7jch92h5wk.png\"\/><\/a><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \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\/articles\/574000\/\"> https:\/\/habr.com\/ru\/articles\/574000\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/pu\/no\/zh\/punozh3zkth3cqyppf_przd5wco.png\" data-src=\"https:\/\/habrastorage.org\/webt\/pu\/no\/zh\/punozh3zkth3cqyppf_przd5wco.png\"\/>  <\/p>\n<p>  \u041f\u0440\u0438\u0432\u0435\u0442, \u0434\u0440\u0443\u0437\u044c\u044f!<\/p>\n<p>  <\/p>\n<p>\u0412 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435 \u0432\u0440\u0435\u043c\u044f \u043c\u043d\u0435 \u043d\u0430 \u0433\u043b\u0430\u0437\u0430 \u0447\u0430\u0441\u0442\u043e \u043f\u043e\u043f\u0430\u0434\u0430\u044e\u0442\u0441\u044f \u0441\u0442\u0430\u0442\u044c\u0438, \u043f\u043e\u0441\u0432\u044f\u0449\u0435\u043d\u043d\u044b\u0435 \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 \u0444\u043e\u0440\u043c\u0430\u043c\u0438 \u0432 <code>React<\/code> \u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0445 \u0445\u0443\u043a\u043e\u0432. \u0412\u043e\u0442 \u043f\u0430\u0440\u043e\u0447\u043a\u0430 \u0442\u0430\u043a\u0438\u0445 \u0441\u0442\u0430\u0442\u0435\u0439:<\/p>\n<p>  <\/p>\n<ul>\n<li><a href=\"https:\/\/daveceddia.com\/react-forms\/\">Using Forms in React<\/a><\/li>\n<li><a href=\"https:\/\/habr.com\/ru\/post\/523256\/\">React.js \u2014 \u0444\u043e\u0440\u043c\u043e\u0448\u043b\u0435\u043f\u0441\u0442\u0432\u043e \u0438\u043b\u0438 \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 \u0444\u043e\u0440\u043c\u0430\u043c\u0438 \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u0445\u0443\u043a\u043e\u0432<\/a><\/li>\n<li><a href=\"https:\/\/felixgerschau.com\/react-hooks-form-validation-typescript\/\">Form validation with React Hooks WITHOUT a library: The Complete Guide<\/a><\/li>\n<\/ul>\n<p>  <\/p>\n<p>\u042f \u0442\u043e\u0436\u0435 \u0440\u0435\u0448\u0438\u043b \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0432 \u044d\u0442\u043e\u043c \u0441\u0432\u043e\u0438 \u0441\u0438\u043b\u044b, \u0447\u0435\u043c\u0443 \u0438 \u043f\u043e\u0441\u0432\u044f\u0449\u0435\u043d\u0430 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f. \u041f\u043e\u0434 &#171;\u044d\u0442\u0438\u043c&#187; \u044f \u043f\u043e\u0434\u0440\u0430\u0437\u0443\u043c\u0435\u0432\u0430\u044e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u043e\u0433\u043e \u0445\u0443\u043a\u0430 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0444\u043e\u0440\u043c\u0430\u043c\u0438 \u0432 <code>React<\/code>.<\/p>\n<p>  <\/p>\n<p>\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043c\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u0445\u0443\u043a, \u0437\u0430\u0442\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0435\u0433\u043e \u0432 \u0444\u043e\u0440\u043c\u0430\u0445 \u0434\u043b\u044f \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.<\/p>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0435\u0439 \u043f\u0440\u0430\u0432\u0434\u043e\u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0441\u0442\u0438 \u043c\u044b \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 express-\u0441\u0435\u0440\u0432\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, jwt-\u0442\u043e\u043a\u0435\u043d \u0438 \u0445\u0435\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043f\u0430\u0440\u043e\u043b\u044c), \u0430 \u0442\u0430\u043a\u0436\u0435 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u0438\u043f\u0438\u0447\u043d\u044b\u0435 \u0434\u043b\u044f \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043e\u0448\u0438\u0431\u043a\u0438 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <code>404 User not found<\/code> \u0438\u043b\u0438 <code>409 Email already in use<\/code>).<\/p>\n<p>  <\/p>\n<p>\u041f\u0440\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0445\u0443\u043a\u0430 \u0438 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043c\u044b \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u043c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044b\u0445 \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435, \u0441\u043c\u0435\u044e \u043d\u0430\u0434\u0435\u044f\u0442\u044c\u0441\u044f, \u043f\u043e\u043a\u0430\u0436\u0443\u0442\u0441\u044f \u0432\u0430\u043c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u043c\u0438.<\/p>\n<p>  <\/p>\n<p>\u041f\u0435\u0441\u043e\u0447\u043d\u0438\u0446\u0430:<\/p>\n<p>  <\/p>\n<div class=\"oembed\">\n<div class=\"tm-iframe_temp\" data-src=\"https:\/\/embedd.srv.habr.com\/iframe\/6120c7c2dc7e91a24fcac46e\" data-style=\"\" id=\"6120c7c2dc7e91a24fcac46e\" width=\"\"><\/div>\n<\/div>\n<p>  <\/p>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0432 \u043f\u0435\u0441\u043e\u0447\u043d\u0438\u0446\u0435 \u0432\u0441\u0435 \u0437\u0430\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e, \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0432\u0432\u0435\u0441\u0442\u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u0443 <code>yarn dev<\/code> \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0435.<\/p>\n<p>  <\/p>\n<p>\u0425\u0443\u043a \u0432 \u0444\u043e\u0440\u043c\u0435 npm-\u043f\u0430\u043a\u0435\u0442\u0430 \u2014 <a href=\"https:\/\/www.npmjs.com\/package\/simple-form-react\"><code>simple-form-react<\/code><\/a>.<\/p>\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-398137","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/398137","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=398137"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/398137\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=398137"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=398137"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=398137"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}