{"id":373604,"date":"2024-05-21T05:40:06","date_gmt":"2024-05-21T05:40:06","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=373604"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=373604","title":{"rendered":"<span>The new code as the side effect<\/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-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<blockquote>\n<p>Correct typing does not guarantee the structure&#8217;s content.<\/p>\n<\/blockquote>\n<p>Is this function a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Functional_programming\" rel=\"noopener noreferrer nofollow\">pure \u00a0<\/a>function?<\/p>\n<pre><code class=\"typescript\">const sum = (a: any, b: any) => a + b;<\/code><\/pre>\n<p>At first glance, yes, however&#8230;<\/p>\n<pre><code class=\"typescript\">const obj = new class {   toString() { return Math.random().toString(36).substring(7) } }  console.log(sum(obj, obj)) \/\/ uretdln9iue console.log(sum(obj, obj)) \/\/ edzlrs4s6l8e console.log(sum(obj, obj)) \/\/ 6l347arllc9q console.log(sum(obj, obj)) \/\/ 529n5l8wn718<\/code><\/pre>\n<p>The correct way to write a pure function would be with typed input parameters&#8230;<\/p>\n<pre><code class=\"typescript\">const sum = (a: number, b: number) => a + b;<\/code><\/pre>\n<p>This works with simple data types. But when attempting to define a complex data type with an interface, there is no guarantee of functional simplicity in the internal implementation, as the interface performs compile-time checks only.<\/p>\n<pre><code class=\"typescript\">interface IObj {   toString(): string; }<\/code><\/pre>\n<p>The code presented below should be called <strong>synchronous<\/strong> \u00a0and <strong>procedural<\/strong> , but not <strong>functional<\/strong> , as the expansion of the set of fields with new code can be called a side effect.<\/p>\n<pre><code class=\"typescript\">import { connect } from 'react-redux' import { formValueSelector } from 'redux-form'  ...  const selector = formValueSelector('selectingFormValues')  SelectingFormValuesForm = connect((state) => {   const hasEmailValue = selector(state, 'hasEmail')   const favoriteColorValue = selector(state, 'favoriteColor')   const { firstName, lastName } = selector(state, 'firstName', 'lastName')   return {     hasEmailValue,     favoriteColorValue,     canSubscribeByEmail: firstName &amp;&amp; lastName,     fullName: `${firstName || ''} ${lastName || ''}` )(SelectingFormValuesForm)<\/code><\/pre>\n<p>The <code>mapStateToProps<\/code> function in this usage has an infinite list of returned values, which expands when the next development iteration updates the interface defining the form and implements a new field. It&#8217;s procedure code, not functional<\/p>\n<p><strong>Solution<\/strong><\/p>\n<blockquote>\n<p>By ensuring the isolation of each field in the form, we can dramatically increase the number of pure functions, eliminating procedural code.<\/p>\n<\/blockquote>\n<pre><code class=\"typescript\">const fields: TypedField[] = [   {     type: FieldType.Text,     name: 'email',     trailingIcon: Email,     defaultValue: 'tripolskypetr@gmail.com',     isInvalid({email}) {         const expr = \/^[\\w-.]+@([\\w-]+\\.)+[\\w-]{2,4}$\/g;         if (!expr.test(email)) {         return 'Invalid email address';         } else {         return null;         }     },     isDisabled({disabled}) {         return disabled;     },     isVisible({visible}) {         return visible;     }   } ]  ...  &lt;One   fields={fields}   onChange={console.log} \/><\/code><\/pre>\n<p>The <code>&lt;One \/><\/code> component can: <\/p>\n<p><u>Render Markup<\/u><\/p>\n<p>By specifying <code>phoneColumns<\/code>, <code>tabletColumns<\/code>, <code>desktopColumns<\/code>. Or simply <code>columns<\/code> if you don&#8217;t want to bother. Fields can be grouped, groups can be nested. <\/p>\n<p><u>Render JSON obtained from Promise<\/u><\/p>\n<p>During loading, a <em>placeholder<\/em> expressed by <a href=\"https:\/\/material-ui.com\/components\/skeleton\/\" rel=\"noopener noreferrer nofollow\">Skeleton \u00a0<\/a>is shown. After loading, the form is displayed. <\/p>\n<p><u>Connect directly to the component&#8217;s state<\/u><\/p>\n<p>Also, `One` can be connected to an object in the component&#8217;s internal state directly. Very convenient, especially when rendering a list of template elements&#8230; <\/p>\n<p><u>Hide, disable, inform the user of input field errors<\/u><\/p>\n<p>By using callbacks <code>isDisabled<\/code>, <code>isVisible<\/code>, <code>isInvalid<\/code>, we can disable, hide, and mark invalid input. Additionally, we can subscribe to <code>focus<\/code> and <code>blur<\/code> events for both individual fields and field groups (bubbling) or the entire <code>One<\/code> component. The field can be marked as <code>readonly<\/code> or specify a <code>compute<\/code> callback for value computation. <\/p>\n<p><u>Automate the creation of internal state<\/u><\/p>\n<p>After declaratively describing the form, working through callbacks is done in a functional style with the passing of the snapshot of the last data object from the input fields (immutable). Also, asynchronous requests can be made if necessary. For text fields, <code>debounce<\/code> has already been applied to optimize the number of input checks&#8230; <\/p>\n<p><u>Single Responsibility Principle<\/u><\/p>\n<p>What if you need to implement the ability to subscribe to an email newsletter (a form consisting of several nested components interacting through a state-props chain), but only for those users who have specified a middle name? Creating a context is potentially unsafe, as it does not guarantee absence of new edits. <\/p>\n<p><u>Notify the application programmer of changes on the form<\/u><\/p>\n<p>By passing a callback to the `One` component&#8217;s input parameter `change`, we can get user input, but the callback will only be called if all validations are successful. <\/p>\n<p><u>Display more than 10 built-in field types and custom components<\/u><\/p>\n<p>Switch (toggle), Line (group outline), Group (field group), Expansion (expandable group), Radio (radio button), Checkbox (checkbox), Text (text field), Progress (progress bar), Slider (slider), Combo (dropdown), Items (multiple selection), Rating (rating), and Component (custom component) are available. <\/p>\n<p><u>Most likely, this list is already outdated<\/u><\/p>\n<p>The component is actively being expanded.<\/p>\n<p><strong>Code Example<\/strong><\/p>\n<blockquote>\n<p>Open <a href=\"https:\/\/jsfiddle.net\/tripolskypetr\/0pywxmsk\/\" rel=\"noopener noreferrer nofollow\">the link<\/a> in your browser&#8230;<\/p>\n<\/blockquote>\n<p>Try experimenting with <code>isDisabled<\/code>, <code>isInvalid<\/code>, <code>isVisible<\/code>. The source code of the <code>&lt;One \/><\/code> component is open and can be viewed <a href=\"https:\/\/github.com\/react-declarative\/react-declarative\/blob\/567f20da6579a9499d3ac413e5f0605b80a774aa\/src\/components\/One\/components\/OneInternal\/OneInternal.tsx\" rel=\"noopener noreferrer nofollow\">here<\/a>.<\/p>\n<pre><code class=\"typescript\">const fields = [   {     type: \"group\",     fields: [       {         type: \"line\",         title: \"Glass Type\"       },       {         title: \"Bronze\",         columns: \"4\",         type: \"checkbox\",         name: \"Bronze\"       },       {         title: \"Matte\",         columns: \"4\",         type: \"checkbox\",         name: \"Matte\"       },       {         title: \"Lightened\",         type: \"checkbox\",         columns: \"4\",         name: \"Lightened\"       },       {         title: \"Transparent\",         type: \"checkbox\",         columns: \"4\",         name: \"Transparent\"       },       {         title: \"Gray\",         columns: \"4\",         type: \"checkbox\",         name: \"Gray\"       },       {         type: \"group\",         fields: [           {             type: \"line\",             title: \"Glass Thickness\"           },           {             title: \"10 mm\",             type: \"radio\",             name: \"Glass Thickness\",             radioValue: \"10 mm\"           },           {             title: \"12 mm\",             type: \"radio\",             name: \"Glass Thickness\",             radioValue: \"12 mm\"           },         ]       }     ]   },   {     type: \"group\",     fields: [       {         type: \"line\",         title: \"Comment\"       },       {         title: \"Comment\",         type: \"text\",         name: \"Comment\",         inputRows: 5,       },     ]   },   {     type: \"group\",     fields: [       {         type: \"line\",         title: \"Delivery Method\"       },       {         title: \"Pickup\",         type: \"switch\",         name: \"Pickup\",         inputType: 'date',       },       {         title: \"Address\",         type: \"text\",         name: \"Address\",         focus, blur,         isDisabled: (obj) => obj[\"Pickup\"]       },     ]   },   {     type: \"line\",     title: \"Slider\",   },   {     type: \"slider\",     name: \"Slider\",     leadingIcon: VolumeDown,     trailingIcon: VolumeUp,     leadingIconClick(v, onChange) { onChange(v - 1) },     trailingIconClick(v, onChange) { onChange(v + 1) }   },   {     type: \"line\",     title: \"Lists\",   },   {     type: \"group\",     fields: [       {         type: \"group\",         columns: \"6\",         fields: [           {             type: \"combo\",             title: \"Item Selection\",             description: \"Selecting one item\",             name: \"Item Selection\",             itemList: [\"Item 1\", \"Item 2\", \"Item 3\"]           },         ]       },       {         type: \"group\",         columns: \"6\",         fields: [           {             type: \"items\",             title: \"Items Selection\",             description: \"Selecting multiple items\",             name: \"Items Selection\",             itemList: [\"Item 1\", \"Item 2\", \"Item 3\"]           },         ]       },     ]   },   {     type: \"line\",     title: \"Custom Component\"   },   {     type: \"component\",     compute: Logger,   },   {     type: \"rating\",     name: \"Rating\"   } ];<\/code><\/pre>\n<\/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\/807635\/\"> https:\/\/habr.com\/ru\/articles\/807635\/<\/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-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<blockquote>\n<p>Correct typing does not guarantee the structure&#8217;s content.<\/p>\n<\/blockquote>\n<p>Is this function a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Functional_programming\" rel=\"noopener noreferrer nofollow\">pure \u00a0<\/a>function?<\/p>\n<pre><code class=\"typescript\">const sum = (a: any, b: any) => a + b;<\/code><\/pre>\n<p>At first glance, yes, however&#8230;<\/p>\n<pre><code class=\"typescript\">const obj = new class {   toString() { return Math.random().toString(36).substring(7) } }  console.log(sum(obj, obj)) \/\/ uretdln9iue console.log(sum(obj, obj)) \/\/ edzlrs4s6l8e console.log(sum(obj, obj)) \/\/ 6l347arllc9q console.log(sum(obj, obj)) \/\/ 529n5l8wn718<\/code><\/pre>\n<p>The correct way to write a pure function would be with typed input parameters&#8230;<\/p>\n<pre><code class=\"typescript\">const sum = (a: number, b: number) => a + b;<\/code><\/pre>\n<p>This works with simple data types. But when attempting to define a complex data type with an interface, there is no guarantee of functional simplicity in the internal implementation, as the interface performs compile-time checks only.<\/p>\n<pre><code class=\"typescript\">interface IObj {   toString(): string; }<\/code><\/pre>\n<p>The code presented below should be called <strong>synchronous<\/strong> \u00a0and <strong>procedural<\/strong> , but not <strong>functional<\/strong> , as the expansion of the set of fields with new code can be called a side effect.<\/p>\n<pre><code class=\"typescript\">import { connect } from 'react-redux' import { formValueSelector } from 'redux-form'  ...  const selector = formValueSelector('selectingFormValues')  SelectingFormValuesForm = connect((state) => {   const hasEmailValue = selector(state, 'hasEmail')   const favoriteColorValue = selector(state, 'favoriteColor')   const { firstName, lastName } = selector(state, 'firstName', 'lastName')   return {     hasEmailValue,     favoriteColorValue,     canSubscribeByEmail: firstName &amp;&amp; lastName,     fullName: `${firstName || ''} ${lastName || ''}` )(SelectingFormValuesForm)<\/code><\/pre>\n<p>The <code>mapStateToProps<\/code> function in this usage has an infinite list of returned values, which expands when the next development iteration updates the interface defining the form and implements a new field. It&#8217;s procedure code, not functional<\/p>\n<p><strong>Solution<\/strong><\/p>\n<blockquote>\n<p>By ensuring the isolation of each field in the form, we can dramatically increase the number of pure functions, eliminating procedural code.<\/p>\n<\/blockquote>\n<pre><code class=\"typescript\">const fields: TypedField[] = [   {     type: FieldType.Text,     name: 'email',     trailingIcon: Email,     defaultValue: 'tripolskypetr@gmail.com',     isInvalid({email}) {         const expr = \/^[\\w-.]+@([\\w-]+\\.)+[\\w-]{2,4}$\/g;         if (!expr.test(email)) {         return 'Invalid email address';         } else {         return null;         }     },     isDisabled({disabled}) {         return disabled;     },     isVisible({visible}) {         return visible;     }   } ]  ...  &lt;One   fields={fields}   onChange={console.log} \/><\/code><\/pre>\n<p>The <code>&lt;One \/><\/code> component can: <\/p>\n<p><u>Render Markup<\/u><\/p>\n<p>By specifying <code>phoneColumns<\/code>, <code>tabletColumns<\/code>, <code>desktopColumns<\/code>. Or simply <code>columns<\/code> if you don&#8217;t want to bother. Fields can be grouped, groups can be nested. <\/p>\n<p><u>Render JSON obtained from Promise<\/u><\/p>\n<p>During loading, a <em>placeholder<\/em> expressed by <a href=\"https:\/\/material-ui.com\/components\/skeleton\/\" rel=\"noopener noreferrer nofollow\">Skeleton \u00a0<\/a>is shown. After loading, the form is displayed. <\/p>\n<p><u>Connect directly to the component&#8217;s state<\/u><\/p>\n<p>Also, `One` can be connected to an object in the component&#8217;s internal state directly. Very convenient, especially when rendering a list of template elements&#8230; <\/p>\n<p><u>Hide, disable, inform the user of input field errors<\/u><\/p>\n<p>By using callbacks <code>isDisabled<\/code>, <code>isVisible<\/code>, <code>isInvalid<\/code>, we can disable, hide, and mark invalid input. Additionally, we can subscribe to <code>focus<\/code> and <code>blur<\/code> events for both individual fields and field groups (bubbling) or the entire <code>One<\/code> component. The field can be marked as <code>readonly<\/code> or specify a <code>compute<\/code> callback for value computation. <\/p>\n<p><u>Automate the creation of internal state<\/u><\/p>\n<p>After declaratively describing the form, working through callbacks is done in a functional style with the passing of the snapshot of the last data object from the input fields (immutable). Also, asynchronous requests can be made if necessary. For text fields, <code>debounce<\/code> has already been applied to optimize the number of input checks&#8230; <\/p>\n<p><u>Single Responsibility Principle<\/u><\/p>\n<p>What if you need to implement the ability to subscribe to an email newsletter (a form consisting of several nested components interacting through a state-props chain), but only for those users who have specified a middle name? Creating a context is potentially unsafe, as it does not guarantee absence of new edits. <\/p>\n<p><u>Notify the application programmer of changes on the form<\/u><\/p>\n<p>By passing a callback to the `One` component&#8217;s input parameter `change`, we can get user input, but the callback will only be called if all validations are successful. <\/p>\n<p><u>Display more than 10 built-in field types and custom components<\/u><\/p>\n<p>Switch (toggle), Line (group outline), Group (field group), Expansion (expandable group), Radio (radio button), Checkbox (checkbox), Text (text field), Progress (progress bar), Slider (slider), Combo (dropdown), Items (multiple selection), Rating (rating), and Component (custom component) are available. <\/p>\n<p><u>Most likely, this list is already outdated<\/u><\/p>\n<p>The component is actively being expanded.<\/p>\n<p><strong>Code Example<\/strong><\/p>\n<blockquote>\n<p>Open <a href=\"https:\/\/jsfiddle.net\/tripolskypetr\/0pywxmsk\/\" rel=\"noopener noreferrer nofollow\">the link<\/a> in your browser&#8230;<\/p>\n<\/blockquote>\n<p>Try experimenting with <code>isDisabled<\/code>, <code>isInvalid<\/code>, <code>isVisible<\/code>. The source code of the <code>&lt;One \/><\/code> component is open and can be viewed <a href=\"https:\/\/github.com\/react-declarative\/react-declarative\/blob\/567f20da6579a9499d3ac413e5f0605b80a774aa\/src\/components\/One\/components\/OneInternal\/OneInternal.tsx\" rel=\"noopener noreferrer nofollow\">here<\/a>.<\/p>\n<pre><code class=\"typescript\">const fields = [   {     type: \"group\",     fields: [       {         type: \"line\",         title: \"Glass Type\"       },       {         title: \"Bronze\",         columns: \"4\",         type: \"checkbox\",         name: \"Bronze\"       },       {         title: \"Matte\",         columns: \"4\",         type: \"checkbox\",         name: \"Matte\"       },       {         title: \"Lightened\",         type: \"checkbox\",         columns: \"4\",         name: \"Lightened\"       },       {         title: \"Transparent\",         type: \"checkbox\",         columns: \"4\",         name: \"Transparent\"       },       {         title: \"Gray\",         columns: \"4\",         type: \"checkbox\",         name: \"Gray\"       },       {         type: \"group\",         fields: [           {             type: \"line\",             title: \"Glass Thickness\"           },           {             title: \"10 mm\",             type: \"radio\",             name: \"Glass Thickness\",             radioValue: \"10 mm\"           },           {             title: \"12 mm\",             type: \"radio\",             name: \"Glass Thickness\",             radioValue: \"12 mm\"           },         ]       }     ]   },   {     type: \"group\",     fields: [       {         type: \"line\",         title: \"Comment\"       },       {         title: \"Comment\",         type: \"text\",         name: \"Comment\",         inputRows: 5,       },     ]   },   {     type: \"group\",     fields: [       {         type: \"line\",         title: \"Delivery Method\"       },       {         title: \"Pickup\",         type: \"switch\",         name: \"Pickup\",         inputType: 'date',       },       {         title: \"Address\",         type: \"text\",         name: \"Address\",         focus, blur,         isDisabled: (obj) => obj[\"Pickup\"]       },     ]   },   {     type: \"line\",     title: \"Slider\",   },   {     type: \"slider\",     name: \"Slider\",     leadingIcon: VolumeDown,     trailingIcon: VolumeUp,     leadingIconClick(v, onChange) { onChange(v - 1) },     trailingIconClick(v, onChange) { onChange(v + 1) }   },   {     type: \"line\",     title: \"Lists\",   },   {     type: \"group\",     fields: [       {         type: \"group\",         columns: \"6\",         fields: [           {             type: \"combo\",             title: \"Item Selection\",             description: \"Selecting one item\",             name: \"Item Selection\",             itemList: [\"Item 1\", \"Item 2\", \"Item 3\"]           },         ]       },       {         type: \"group\",         columns: \"6\",         fields: [           {             type: \"items\",             title: \"Items Selection\",             description: \"Selecting multiple items\",             name: \"Items Selection\",             itemList: [\"Item 1\", \"Item 2\", \"Item 3\"]           },         ]       },     ]   },   {     type: \"line\",     title: \"Custom Component\"   },   {     type: \"component\",     compute: Logger,   },   {     type: \"rating\",     name: \"Rating\"   } ];<\/code><\/pre>\n<\/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\/807635\/\"> https:\/\/habr.com\/ru\/articles\/807635\/<\/a><br \/><\/br><\/br><\/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-373604","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/373604","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=373604"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/373604\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=373604"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=373604"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=373604"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}