From b320809a751ab6d56cde14b13da04f17ecbe6598 Mon Sep 17 00:00:00 2001 From: igorkamyshev Date: Wed, 31 Jan 2024 11:29:26 +0000 Subject: [PATCH] =?UTF-8?q?Deploying=20to=20gh-pages=20from=20@=20effector?= =?UTF-8?q?/patronum@d2d7ee75fbafb7939900c08e504fe426700f965c=20?= =?UTF-8?q?=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 404.html | 4 ++-- assets/js/{1178bc06.b61c1d3d.js => 1178bc06.04a79afb.js} | 2 +- .../{runtime~main.cc8a4be3.js => runtime~main.c7375206.js} | 2 +- docs/installation/index.html | 4 ++-- docs/migration-guide/index.html | 4 ++-- index.html | 4 ++-- methods/and/index.html | 4 ++-- methods/combine-events/index.html | 4 ++-- methods/condition/index.html | 4 ++-- methods/debounce/index.html | 4 ++-- methods/debug/index.html | 4 ++-- methods/delay/index.html | 4 ++-- methods/either/index.html | 4 ++-- methods/empty/index.html | 4 ++-- methods/equals/index.html | 4 ++-- methods/every/index.html | 4 ++-- methods/format/index.html | 4 ++-- methods/in-flight/index.html | 4 ++-- methods/index.html | 4 ++-- methods/interval/index.html | 4 ++-- methods/not/index.html | 4 ++-- methods/once/index.html | 4 ++-- methods/or/index.html | 4 ++-- methods/pending/index.html | 4 ++-- methods/previous/index.html | 4 ++-- methods/readonly/index.html | 4 ++-- methods/reset/index.html | 4 ++-- methods/reshape/index.html | 4 ++-- methods/snapshot/index.html | 4 ++-- methods/some/index.html | 4 ++-- methods/split-map/index.html | 4 ++-- methods/spread/index.html | 6 +++--- methods/status/index.html | 4 ++-- methods/throttle/index.html | 4 ++-- methods/time/index.html | 4 ++-- search/index.html | 4 ++-- 36 files changed, 71 insertions(+), 71 deletions(-) rename assets/js/{1178bc06.b61c1d3d.js => 1178bc06.04a79afb.js} (52%) rename assets/js/{runtime~main.cc8a4be3.js => runtime~main.c7375206.js} (98%) diff --git a/404.html b/404.html index 78b8ea48..fecab1c1 100644 --- a/404.html +++ b/404.html @@ -4,13 +4,13 @@ Page Not Found | effector patronum - +
Skip to main content

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

- + \ No newline at end of file diff --git a/assets/js/1178bc06.b61c1d3d.js b/assets/js/1178bc06.04a79afb.js similarity index 52% rename from assets/js/1178bc06.b61c1d3d.js rename to assets/js/1178bc06.04a79afb.js index 7247f935..3a4b00d6 100644 --- a/assets/js/1178bc06.b61c1d3d.js +++ b/assets/js/1178bc06.04a79afb.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[502],{4852:(e,t,r)=>{r.d(t,{Zo:()=>p,kt:()=>g});var a=r(9231);function n(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,a)}return r}function i(e){for(var t=1;t=0||(n[r]=e[r]);return n}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(n[r]=e[r])}return n}var s=a.createContext({}),d=function(e){var t=a.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},p=function(e){var t=d(e.components);return a.createElement(s.Provider,{value:t},e.children)},u="mdxType",c={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var r=e.components,n=e.mdxType,o=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=d(r),m=n,g=u["".concat(s,".").concat(m)]||u[m]||c[m]||o;return r?a.createElement(g,i(i({ref:t},p),{},{components:r})):a.createElement(g,i({ref:t},p))}));function g(e,t){var r=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var o=r.length,i=new Array(o);i[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[u]="string"==typeof e?e:n,i[1]=l;for(var d=2;d{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>i,default:()=>c,frontMatter:()=>o,metadata:()=>l,toc:()=>d});var a=r(4011),n=(r(9231),r(4852));const o={},i="spread",l={unversionedId:"spread/readme",id:"spread/readme",title:"spread",description:"source = spread(targets)",source:"@site/../src/spread/readme.md",sourceDirName:"spread",slug:"/spread/",permalink:"/methods/spread/",draft:!1,editUrl:"https://github.com/effector/patronum/tree/main/src/../src/spread/readme.md",tags:[],version:"current",frontMatter:{},sidebar:"methodsSidebar",previous:{title:"splitMap",permalink:"/methods/split-map/"},next:{title:"status",permalink:"/methods/status/"}},s={},d=[{value:"source = spread(targets)",id:"source--spreadtargets",level:2},{value:"Motivation",id:"motivation",level:3},{value:"Formulae",id:"formulae",level:3},{value:"Arguments",id:"arguments",level:3},{value:"Returns",id:"returns",level:3},{value:"Example",id:"example",level:3},{value:"Conditionally save value to stores",id:"conditionally-save-value-to-stores",level:4},{value:"Nested spreading",id:"nested-spreading",level:4},{value:"spread({ source, targets })",id:"spread-source-targets-",level:2},{value:"Motivation",id:"motivation-1",level:3},{value:"Formulae",id:"formulae-1",level:3},{value:"Arguments",id:"arguments-1",level:3},{value:"Example",id:"example-1",level:3},{value:"Save fields of payload to different stores",id:"save-fields-of-payload-to-different-stores",level:4},{value:"Call events on store changes",id:"call-events-on-store-changes",level:4},{value:"source = spread({ targets })",id:"source--spread-targets-",level:2},{value:"Motivation",id:"motivation-2",level:3},{value:"Formulae",id:"formulae-2",level:3},{value:"Arguments",id:"arguments-2",level:3},{value:"Returns",id:"returns-1",level:3}],p={toc:d},u="wrapper";function c(e){let{components:t,...r}=e;return(0,n.kt)(u,(0,a.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("h1",{id:"spread"},"spread"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-ts"},"import { spread } from 'patronum';\n// or\nimport { spread } from 'patronum/spread';\n")),(0,n.kt)("h2",{id:"source--spreadtargets"},(0,n.kt)("inlineCode",{parentName:"h2"},"source = spread(targets)")),(0,n.kt)("admonition",{title:"since",type:"note"},(0,n.kt)("p",{parentName:"admonition"},"patronum 2.1.0\nUse ",(0,n.kt)("inlineCode",{parentName:"p"},"spread({ targets })")," with patronum < 2.1.0")),(0,n.kt)("h3",{id:"motivation"},"Motivation"),(0,n.kt)("p",null,"This method allows to trigger many target at once, if they match the source structure.\nIt is useful when you need to destructure object and save values to different stores."),(0,n.kt)("h3",{id:"formulae"},"Formulae"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-ts"},"source = spread({ field: target, ... })\n")),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"When ",(0,n.kt)("inlineCode",{parentName:"li"},"source")," is triggered with ",(0,n.kt)("strong",{parentName:"li"},"object"),", extract ",(0,n.kt)("inlineCode",{parentName:"li"},"field")," from data, and trigger ",(0,n.kt)("inlineCode",{parentName:"li"},"target")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("inlineCode",{parentName:"li"},"targets")," can have multiple properties"),(0,n.kt)("li",{parentName:"ul"},"If the ",(0,n.kt)("inlineCode",{parentName:"li"},"source")," was triggered with non-object, nothing would be happening"),(0,n.kt)("li",{parentName:"ul"},"If ",(0,n.kt)("inlineCode",{parentName:"li"},"source")," is triggered with object but without propertpy ",(0,n.kt)("inlineCode",{parentName:"li"},"field"),", target for this ",(0,n.kt)("inlineCode",{parentName:"li"},"field")," will not be triggered")),(0,n.kt)("h3",{id:"arguments"},"Arguments"),(0,n.kt)("ol",null,(0,n.kt)("li",{parentName:"ol"},(0,n.kt)("inlineCode",{parentName:"li"},"targets")," ",(0,n.kt)("inlineCode",{parentName:"li"},"(Record | Store | Effect>)")," \u2014 Flat object which key is key in ",(0,n.kt)("inlineCode",{parentName:"li"},"source")," payload, and value is unit to store value to.")),(0,n.kt)("h3",{id:"returns"},"Returns"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("inlineCode",{parentName:"li"},"source")," ",(0,n.kt)("inlineCode",{parentName:"li"},"(Event)")," \u2014 Source event, data passed to it should be an object with fields from ",(0,n.kt)("inlineCode",{parentName:"li"},"targets"))),(0,n.kt)("h3",{id:"example"},"Example"),(0,n.kt)("h4",{id:"conditionally-save-value-to-stores"},"Conditionally save value to stores"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-ts"},"import { createStore, createEvent, sample } from 'effector';\nimport { spread } from 'patronum';\n\nconst $first = createStore('');\nconst $last = createStore('');\n\nconst formReceived = createEvent();\n\nsample({\n source: formReceived,\n filter: (form) => form.first.length > 0 && form.last.length > 0,\n target: spread({\n first: $first,\n last: $last,\n }),\n});\n\n$first.watch((first) => console.log('First name', first));\n$last.watch((last) => console.log('Last name', last));\n\nformReceived({ first: '', last: '' });\n// Nothing, because filter returned true\n\nformReceived({ first: 'Hello', last: 'World' });\n// => First name Hello\n// => Last name World\n")),(0,n.kt)("h4",{id:"nested-spreading"},"Nested spreading"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-ts"},"const $targetA = createStore('');\nconst $targetB = createStore(0);\nconst $targetC = createStore(false);\n\nconst trigger = spread({\n first: $targetA,\n second: spread({\n foo: $targetB,\n bar: $targetC,\n }),\n});\n\n$targetA.watch((payload) => console.log('targetA', payload));\n$targetB.watch((payload) => console.log('targetB', payload));\n$targetC.watch((payload) => console.log('targetC', payload));\n\ntrigger({\n first: 'Hello',\n second: {\n foo: 200,\n bar: true,\n },\n});\n// => targetA Hello\n// => targetB 200\n// => targetC true\n")),(0,n.kt)("h2",{id:"spread-source-targets-"},(0,n.kt)("inlineCode",{parentName:"h2"},"spread({ source, targets })")),(0,n.kt)("h3",{id:"motivation-1"},"Motivation"),(0,n.kt)("p",null,"This method allows to trigger many target at once, if they match the source structure.\nIt is useful when you need to destructure object and save values to different stores."),(0,n.kt)("h3",{id:"formulae-1"},"Formulae"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-ts"},"spread({ source, targets: { field: target, ... } })\n")),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"When ",(0,n.kt)("inlineCode",{parentName:"li"},"source")," is triggered with ",(0,n.kt)("strong",{parentName:"li"},"object"),", extract ",(0,n.kt)("inlineCode",{parentName:"li"},"field")," from data, and trigger ",(0,n.kt)("inlineCode",{parentName:"li"},"target")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("inlineCode",{parentName:"li"},"targets")," can have multiple properties"),(0,n.kt)("li",{parentName:"ul"},"If the ",(0,n.kt)("inlineCode",{parentName:"li"},"source")," was triggered with non-object, nothing would be happening"),(0,n.kt)("li",{parentName:"ul"},"If ",(0,n.kt)("inlineCode",{parentName:"li"},"source")," is triggered with object but without propertpy ",(0,n.kt)("inlineCode",{parentName:"li"},"field"),", target for this ",(0,n.kt)("inlineCode",{parentName:"li"},"field")," will not be triggered")),(0,n.kt)("h3",{id:"arguments-1"},"Arguments"),(0,n.kt)("ol",null,(0,n.kt)("li",{parentName:"ol"},(0,n.kt)("inlineCode",{parentName:"li"},"source")," ",(0,n.kt)("inlineCode",{parentName:"li"},"(Event")," | ",(0,n.kt)("inlineCode",{parentName:"li"},"Store")," | ",(0,n.kt)("inlineCode",{parentName:"li"},"Effect)")," \u2014 Source unit, data passed to it should be an object with fields from ",(0,n.kt)("inlineCode",{parentName:"li"},"targets")),(0,n.kt)("li",{parentName:"ol"},(0,n.kt)("inlineCode",{parentName:"li"},"targets")," ",(0,n.kt)("inlineCode",{parentName:"li"},"(Record | Store | Effect>)")," \u2014 Flat object which key is key in ",(0,n.kt)("inlineCode",{parentName:"li"},"source")," payload, and value is unit to store value to.")),(0,n.kt)("h3",{id:"example-1"},"Example"),(0,n.kt)("h4",{id:"save-fields-of-payload-to-different-stores"},"Save fields of payload to different stores"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-ts"},"import { createStore, createEvent } from 'effector';\nimport { spread } from 'patronum';\n\nconst $first = createStore('');\nconst $last = createStore('');\n\nconst formReceived = createEvent();\n\nspread({\n source: formReceived,\n targets: {\n first: $first,\n last: $last,\n },\n});\n\n$first.watch((first) => console.log('First name', first));\n$last.watch((last) => console.log('Last name', last));\n\nformReceived({ first: 'Sergey', last: 'Sova' });\n// => First name Sergey\n// => Last name Sova\n\nformReceived({ first: 'Patronum' });\n// => First name Patronum\n")),(0,n.kt)("h4",{id:"call-events-on-store-changes"},"Call events on store changes"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-ts"},"import { createStore, createEvent } from 'effector';\nimport { spread } from 'patronum/spread';\n\nconst save = createEvent();\nconst $form = createStore(null).on(save, (_, form) => form);\n\nconst firstNameChanged = createEvent();\nconst lastNameChanged = createEvent();\n\nspread({\n source: $form,\n targets: {\n first: firstNameChanged,\n last: lastNameChanged,\n },\n});\n\nfirstNameChanged.watch((first) => console.log('First name', first));\nlastNameChanged.watch((last) => console.log('Last name', last));\n\nsave({ first: 'Sergey', last: 'Sova' });\n// => First name Sergey\n// => Last name Sova\n\nsave(null);\n// Nothing, because store is null\n")),(0,n.kt)("h2",{id:"source--spread-targets-"},(0,n.kt)("inlineCode",{parentName:"h2"},"source = spread({ targets })")),(0,n.kt)("h3",{id:"motivation-2"},"Motivation"),(0,n.kt)("p",null,"This overload recieves ",(0,n.kt)("inlineCode",{parentName:"p"},"targets")," as an object. May be useful for additional clarity, but it's longer to write"),(0,n.kt)("h3",{id:"formulae-2"},"Formulae"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-ts"},"source = spread({ targets: { field: target, ... } })\n")),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"When ",(0,n.kt)("inlineCode",{parentName:"li"},"source")," is triggered with ",(0,n.kt)("strong",{parentName:"li"},"object"),", extract ",(0,n.kt)("inlineCode",{parentName:"li"},"field")," from data, and trigger ",(0,n.kt)("inlineCode",{parentName:"li"},"target")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("inlineCode",{parentName:"li"},"targets")," can have multiple properties"),(0,n.kt)("li",{parentName:"ul"},"If the ",(0,n.kt)("inlineCode",{parentName:"li"},"source")," was triggered with non-object, nothing would be happening"),(0,n.kt)("li",{parentName:"ul"},"If ",(0,n.kt)("inlineCode",{parentName:"li"},"source")," is triggered with object but without propertpy ",(0,n.kt)("inlineCode",{parentName:"li"},"field"),", target for this ",(0,n.kt)("inlineCode",{parentName:"li"},"field")," will not be triggered")),(0,n.kt)("h3",{id:"arguments-2"},"Arguments"),(0,n.kt)("ol",null,(0,n.kt)("li",{parentName:"ol"},(0,n.kt)("inlineCode",{parentName:"li"},"targets")," ",(0,n.kt)("inlineCode",{parentName:"li"},"(Record | Store | Effect>)")," \u2014 Flat object which key is key in ",(0,n.kt)("inlineCode",{parentName:"li"},"source")," payload, and value is unit to store value to.")),(0,n.kt)("h3",{id:"returns-1"},"Returns"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("inlineCode",{parentName:"li"},"source")," ",(0,n.kt)("inlineCode",{parentName:"li"},"(Event)")," \u2014 Source event, data passed to it should be an object with fields from ",(0,n.kt)("inlineCode",{parentName:"li"},"targets"))))}c.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[502],{4852:(e,t,r)=>{r.d(t,{Zo:()=>p,kt:()=>g});var a=r(9231);function n(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,a)}return r}function i(e){for(var t=1;t=0||(n[r]=e[r]);return n}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(n[r]=e[r])}return n}var s=a.createContext({}),d=function(e){var t=a.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},p=function(e){var t=d(e.components);return a.createElement(s.Provider,{value:t},e.children)},u="mdxType",c={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var r=e.components,n=e.mdxType,o=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),u=d(r),m=n,g=u["".concat(s,".").concat(m)]||u[m]||c[m]||o;return r?a.createElement(g,i(i({ref:t},p),{},{components:r})):a.createElement(g,i({ref:t},p))}));function g(e,t){var r=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var o=r.length,i=new Array(o);i[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[u]="string"==typeof e?e:n,i[1]=l;for(var d=2;d{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>i,default:()=>c,frontMatter:()=>o,metadata:()=>l,toc:()=>d});var a=r(4011),n=(r(9231),r(4852));const o={},i="spread",l={unversionedId:"spread/readme",id:"spread/readme",title:"spread",description:"source = spread(targets)",source:"@site/../src/spread/readme.md",sourceDirName:"spread",slug:"/spread/",permalink:"/methods/spread/",draft:!1,editUrl:"https://github.com/effector/patronum/tree/main/src/../src/spread/readme.md",tags:[],version:"current",frontMatter:{},sidebar:"methodsSidebar",previous:{title:"splitMap",permalink:"/methods/split-map/"},next:{title:"status",permalink:"/methods/status/"}},s={},d=[{value:"source = spread(targets)",id:"source--spreadtargets",level:2},{value:"Motivation",id:"motivation",level:3},{value:"Formulae",id:"formulae",level:3},{value:"Arguments",id:"arguments",level:3},{value:"Returns",id:"returns",level:3},{value:"Example",id:"example",level:3},{value:"Conditionally save value to stores",id:"conditionally-save-value-to-stores",level:4},{value:"Nested spreading",id:"nested-spreading",level:4},{value:"spread({ source, targets })",id:"spread-source-targets-",level:2},{value:"Motivation",id:"motivation-1",level:3},{value:"Formulae",id:"formulae-1",level:3},{value:"Arguments",id:"arguments-1",level:3},{value:"Example",id:"example-1",level:3},{value:"Save fields of payload to different stores",id:"save-fields-of-payload-to-different-stores",level:4},{value:"Call events on store changes",id:"call-events-on-store-changes",level:4},{value:"source = spread({ targets })",id:"source--spread-targets-",level:2},{value:"Motivation",id:"motivation-2",level:3},{value:"Formulae",id:"formulae-2",level:3},{value:"Arguments",id:"arguments-2",level:3},{value:"Returns",id:"returns-1",level:3}],p={toc:d},u="wrapper";function c(e){let{components:t,...r}=e;return(0,n.kt)(u,(0,a.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("h1",{id:"spread"},"spread"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-ts"},"import { spread } from 'patronum';\n// or\nimport { spread } from 'patronum/spread';\n")),(0,n.kt)("h2",{id:"source--spreadtargets"},(0,n.kt)("inlineCode",{parentName:"h2"},"source = spread(targets)")),(0,n.kt)("admonition",{title:"since",type:"note"},(0,n.kt)("p",{parentName:"admonition"},"patronum 2.1.0\nUse ",(0,n.kt)("inlineCode",{parentName:"p"},"spread({ targets })")," with patronum < 2.1.0")),(0,n.kt)("h3",{id:"motivation"},"Motivation"),(0,n.kt)("p",null,"This method allows to trigger many target at once, if they match the source structure.\nIt is useful when you need to destructure object and save values to different stores."),(0,n.kt)("h3",{id:"formulae"},"Formulae"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-ts"},"source = spread({ field: target, ... })\n")),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"When ",(0,n.kt)("inlineCode",{parentName:"li"},"source")," is triggered with ",(0,n.kt)("strong",{parentName:"li"},"object"),", extract ",(0,n.kt)("inlineCode",{parentName:"li"},"field")," from data, and trigger ",(0,n.kt)("inlineCode",{parentName:"li"},"target")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("inlineCode",{parentName:"li"},"targets")," can have multiple properties"),(0,n.kt)("li",{parentName:"ul"},"If the ",(0,n.kt)("inlineCode",{parentName:"li"},"source")," was triggered with non-object, nothing would be happening"),(0,n.kt)("li",{parentName:"ul"},"If ",(0,n.kt)("inlineCode",{parentName:"li"},"source")," is triggered with object but without propertpy ",(0,n.kt)("inlineCode",{parentName:"li"},"field"),", target for this ",(0,n.kt)("inlineCode",{parentName:"li"},"field")," will not be triggered")),(0,n.kt)("h3",{id:"arguments"},"Arguments"),(0,n.kt)("ol",null,(0,n.kt)("li",{parentName:"ol"},(0,n.kt)("inlineCode",{parentName:"li"},"targets")," ",(0,n.kt)("inlineCode",{parentName:"li"},"(Record | Store | Effect>)")," \u2014 Flat object which key is key in ",(0,n.kt)("inlineCode",{parentName:"li"},"source")," payload, and value is unit to store value to.")),(0,n.kt)("h3",{id:"returns"},"Returns"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("inlineCode",{parentName:"li"},"source")," ",(0,n.kt)("inlineCode",{parentName:"li"},"(Event)")," \u2014 Source event, data passed to it should be an object with fields from ",(0,n.kt)("inlineCode",{parentName:"li"},"targets"))),(0,n.kt)("h3",{id:"example"},"Example"),(0,n.kt)("h4",{id:"conditionally-save-value-to-stores"},"Conditionally save value to stores"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-ts"},"import { createStore, createEvent, sample } from 'effector';\nimport { spread } from 'patronum';\n\nconst $first = createStore('');\nconst $last = createStore('');\n\nconst formReceived = createEvent();\n\nsample({\n source: formReceived,\n filter: (form) => form.first.length > 0 && form.last.length > 0,\n target: spread({\n first: $first,\n last: $last,\n }),\n});\n\n$first.watch((first) => console.log('First name', first));\n$last.watch((last) => console.log('Last name', last));\n\nformReceived({ first: '', last: '' });\n// Nothing, because filter returned false\n\nformReceived({ first: 'Hello', last: 'World' });\n// => First name Hello\n// => Last name World\n")),(0,n.kt)("h4",{id:"nested-spreading"},"Nested spreading"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-ts"},"const $targetA = createStore('');\nconst $targetB = createStore(0);\nconst $targetC = createStore(false);\n\nconst trigger = spread({\n first: $targetA,\n second: spread({\n foo: $targetB,\n bar: $targetC,\n }),\n});\n\n$targetA.watch((payload) => console.log('targetA', payload));\n$targetB.watch((payload) => console.log('targetB', payload));\n$targetC.watch((payload) => console.log('targetC', payload));\n\ntrigger({\n first: 'Hello',\n second: {\n foo: 200,\n bar: true,\n },\n});\n// => targetA Hello\n// => targetB 200\n// => targetC true\n")),(0,n.kt)("h2",{id:"spread-source-targets-"},(0,n.kt)("inlineCode",{parentName:"h2"},"spread({ source, targets })")),(0,n.kt)("h3",{id:"motivation-1"},"Motivation"),(0,n.kt)("p",null,"This method allows to trigger many target at once, if they match the source structure.\nIt is useful when you need to destructure object and save values to different stores."),(0,n.kt)("h3",{id:"formulae-1"},"Formulae"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-ts"},"spread({ source, targets: { field: target, ... } })\n")),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"When ",(0,n.kt)("inlineCode",{parentName:"li"},"source")," is triggered with ",(0,n.kt)("strong",{parentName:"li"},"object"),", extract ",(0,n.kt)("inlineCode",{parentName:"li"},"field")," from data, and trigger ",(0,n.kt)("inlineCode",{parentName:"li"},"target")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("inlineCode",{parentName:"li"},"targets")," can have multiple properties"),(0,n.kt)("li",{parentName:"ul"},"If the ",(0,n.kt)("inlineCode",{parentName:"li"},"source")," was triggered with non-object, nothing would be happening"),(0,n.kt)("li",{parentName:"ul"},"If ",(0,n.kt)("inlineCode",{parentName:"li"},"source")," is triggered with object but without propertpy ",(0,n.kt)("inlineCode",{parentName:"li"},"field"),", target for this ",(0,n.kt)("inlineCode",{parentName:"li"},"field")," will not be triggered")),(0,n.kt)("h3",{id:"arguments-1"},"Arguments"),(0,n.kt)("ol",null,(0,n.kt)("li",{parentName:"ol"},(0,n.kt)("inlineCode",{parentName:"li"},"source")," ",(0,n.kt)("inlineCode",{parentName:"li"},"(Event")," | ",(0,n.kt)("inlineCode",{parentName:"li"},"Store")," | ",(0,n.kt)("inlineCode",{parentName:"li"},"Effect)")," \u2014 Source unit, data passed to it should be an object with fields from ",(0,n.kt)("inlineCode",{parentName:"li"},"targets")),(0,n.kt)("li",{parentName:"ol"},(0,n.kt)("inlineCode",{parentName:"li"},"targets")," ",(0,n.kt)("inlineCode",{parentName:"li"},"(Record | Store | Effect>)")," \u2014 Flat object which key is key in ",(0,n.kt)("inlineCode",{parentName:"li"},"source")," payload, and value is unit to store value to.")),(0,n.kt)("h3",{id:"example-1"},"Example"),(0,n.kt)("h4",{id:"save-fields-of-payload-to-different-stores"},"Save fields of payload to different stores"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-ts"},"import { createStore, createEvent } from 'effector';\nimport { spread } from 'patronum';\n\nconst $first = createStore('');\nconst $last = createStore('');\n\nconst formReceived = createEvent();\n\nspread({\n source: formReceived,\n targets: {\n first: $first,\n last: $last,\n },\n});\n\n$first.watch((first) => console.log('First name', first));\n$last.watch((last) => console.log('Last name', last));\n\nformReceived({ first: 'Sergey', last: 'Sova' });\n// => First name Sergey\n// => Last name Sova\n\nformReceived({ first: 'Patronum' });\n// => First name Patronum\n")),(0,n.kt)("h4",{id:"call-events-on-store-changes"},"Call events on store changes"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-ts"},"import { createStore, createEvent } from 'effector';\nimport { spread } from 'patronum/spread';\n\nconst save = createEvent();\nconst $form = createStore(null).on(save, (_, form) => form);\n\nconst firstNameChanged = createEvent();\nconst lastNameChanged = createEvent();\n\nspread({\n source: $form,\n targets: {\n first: firstNameChanged,\n last: lastNameChanged,\n },\n});\n\nfirstNameChanged.watch((first) => console.log('First name', first));\nlastNameChanged.watch((last) => console.log('Last name', last));\n\nsave({ first: 'Sergey', last: 'Sova' });\n// => First name Sergey\n// => Last name Sova\n\nsave(null);\n// Nothing, because store is null\n")),(0,n.kt)("h2",{id:"source--spread-targets-"},(0,n.kt)("inlineCode",{parentName:"h2"},"source = spread({ targets })")),(0,n.kt)("h3",{id:"motivation-2"},"Motivation"),(0,n.kt)("p",null,"This overload recieves ",(0,n.kt)("inlineCode",{parentName:"p"},"targets")," as an object. May be useful for additional clarity, but it's longer to write"),(0,n.kt)("h3",{id:"formulae-2"},"Formulae"),(0,n.kt)("pre",null,(0,n.kt)("code",{parentName:"pre",className:"language-ts"},"source = spread({ targets: { field: target, ... } })\n")),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"When ",(0,n.kt)("inlineCode",{parentName:"li"},"source")," is triggered with ",(0,n.kt)("strong",{parentName:"li"},"object"),", extract ",(0,n.kt)("inlineCode",{parentName:"li"},"field")," from data, and trigger ",(0,n.kt)("inlineCode",{parentName:"li"},"target")),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("inlineCode",{parentName:"li"},"targets")," can have multiple properties"),(0,n.kt)("li",{parentName:"ul"},"If the ",(0,n.kt)("inlineCode",{parentName:"li"},"source")," was triggered with non-object, nothing would be happening"),(0,n.kt)("li",{parentName:"ul"},"If ",(0,n.kt)("inlineCode",{parentName:"li"},"source")," is triggered with object but without propertpy ",(0,n.kt)("inlineCode",{parentName:"li"},"field"),", target for this ",(0,n.kt)("inlineCode",{parentName:"li"},"field")," will not be triggered")),(0,n.kt)("h3",{id:"arguments-2"},"Arguments"),(0,n.kt)("ol",null,(0,n.kt)("li",{parentName:"ol"},(0,n.kt)("inlineCode",{parentName:"li"},"targets")," ",(0,n.kt)("inlineCode",{parentName:"li"},"(Record | Store | Effect>)")," \u2014 Flat object which key is key in ",(0,n.kt)("inlineCode",{parentName:"li"},"source")," payload, and value is unit to store value to.")),(0,n.kt)("h3",{id:"returns-1"},"Returns"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("inlineCode",{parentName:"li"},"source")," ",(0,n.kt)("inlineCode",{parentName:"li"},"(Event)")," \u2014 Source event, data passed to it should be an object with fields from ",(0,n.kt)("inlineCode",{parentName:"li"},"targets"))))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/runtime~main.cc8a4be3.js b/assets/js/runtime~main.c7375206.js similarity index 98% rename from assets/js/runtime~main.cc8a4be3.js rename to assets/js/runtime~main.c7375206.js index f19fc439..7cf27860 100644 --- a/assets/js/runtime~main.cc8a4be3.js +++ b/assets/js/runtime~main.c7375206.js @@ -1 +1 @@ -(()=>{"use strict";var e,a,t,r,d,f={},c={};function o(e){var a=c[e];if(void 0!==a)return a.exports;var t=c[e]={id:e,loaded:!1,exports:{}};return f[e].call(t.exports,t,t.exports,o),t.loaded=!0,t.exports}o.m=f,o.c=c,e=[],o.O=(a,t,r,d)=>{if(!t){var f=1/0;for(i=0;i=d)&&Object.keys(o.O).every((e=>o.O[e](t[n])))?t.splice(n--,1):(c=!1,d0&&e[i-1][2]>d;i--)e[i]=e[i-1];e[i]=[t,r,d]},o.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return o.d(a,{a:a}),a},t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,o.t=function(e,r){if(1&r&&(e=this(e)),8&r)return e;if("object"==typeof e&&e){if(4&r&&e.__esModule)return e;if(16&r&&"function"==typeof e.then)return e}var d=Object.create(null);o.r(d);var f={};a=a||[null,t({}),t([]),t(t)];for(var c=2&r&&e;"object"==typeof c&&!~a.indexOf(c);c=t(c))Object.getOwnPropertyNames(c).forEach((a=>f[a]=()=>e[a]));return f.default=()=>e,o.d(d,f),d},o.d=(e,a)=>{for(var t in a)o.o(a,t)&&!o.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:a[t]})},o.f={},o.e=e=>Promise.all(Object.keys(o.f).reduce(((a,t)=>(o.f[t](e,a),a)),[])),o.u=e=>"assets/js/"+({34:"5b0e0e08",35:"b41febc4",53:"935f2afb",84:"9dc53199",85:"1f391b9e",109:"c3a18047",151:"6256d5c3",153:"97ffe59f",169:"c7a80478",172:"978828ab",222:"08f30e7d",231:"84071e39",236:"e09d216d",237:"1df93b7f",299:"33c22581",485:"ad512b16",493:"deda2cf3",502:"1178bc06",514:"1be78505",549:"f304a758",585:"a9298e25",609:"8d76850e",613:"f6536843",662:"7921cf10",687:"308aca62",732:"a7fdbc71",774:"4af54caf",777:"48e94e84",778:"e55f1e0e",817:"2b95ca9d",825:"a765e1a2",873:"67c026b6",876:"4d9d995f",885:"8d0041db",896:"08154ee9",900:"1ae5d93b",916:"e9221267",918:"17896441",920:"1a4e3797",945:"c9ae8b2d"}[e]||e)+"."+{34:"975d4ae9",35:"fb3c65a7",53:"b5055a79",84:"13b40e3c",85:"48bd894a",109:"9ecd08cc",151:"49478c67",153:"88cff6ef",169:"a02425b0",172:"1ad8f25b",222:"ad0ac244",231:"321abdbd",236:"5c109b9d",237:"52a1d67f",299:"de5322e1",485:"2de7ded0",493:"6f2a713d",502:"b61c1d3d",514:"51b45561",530:"f5cc4ef9",549:"756c8e8b",585:"a535df35",609:"662cf357",613:"20f6a121",638:"b54116d7",654:"d636b64d",662:"aaddc73d",687:"b045aa80",732:"8c5cb0f4",774:"0b1ea9c3",777:"57154658",778:"1e3aaf8e",817:"d3b76552",825:"b6852205",873:"8e6669d1",876:"aa00eebb",885:"2d0bbcd6",896:"011f7e3f",900:"85fac64d",901:"5fd7132c",916:"8faa3552",918:"98c8b99a",920:"67205d40",945:"e7e15d92",967:"d43ac743"}[e]+".js",o.miniCssF=e=>{},o.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),o.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),r={},d="website:",o.l=(e,a,t,f)=>{if(r[e])r[e].push(a);else{var c,n;if(void 0!==t)for(var b=document.getElementsByTagName("script"),i=0;i{c.onerror=c.onload=null,clearTimeout(s);var d=r[e];if(delete r[e],c.parentNode&&c.parentNode.removeChild(c),d&&d.forEach((e=>e(t))),a)return a(t)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:c}),12e4);c.onerror=l.bind(null,c.onerror),c.onload=l.bind(null,c.onload),n&&document.head.appendChild(c)}},o.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.p="/",o.gca=function(e){return e={17896441:"918","5b0e0e08":"34",b41febc4:"35","935f2afb":"53","9dc53199":"84","1f391b9e":"85",c3a18047:"109","6256d5c3":"151","97ffe59f":"153",c7a80478:"169","978828ab":"172","08f30e7d":"222","84071e39":"231",e09d216d:"236","1df93b7f":"237","33c22581":"299",ad512b16:"485",deda2cf3:"493","1178bc06":"502","1be78505":"514",f304a758:"549",a9298e25:"585","8d76850e":"609",f6536843:"613","7921cf10":"662","308aca62":"687",a7fdbc71:"732","4af54caf":"774","48e94e84":"777",e55f1e0e:"778","2b95ca9d":"817",a765e1a2:"825","67c026b6":"873","4d9d995f":"876","8d0041db":"885","08154ee9":"896","1ae5d93b":"900",e9221267:"916","1a4e3797":"920",c9ae8b2d:"945"}[e]||e,o.p+o.u(e)},(()=>{var e={303:0,532:0};o.f.j=(a,t)=>{var r=o.o(e,a)?e[a]:void 0;if(0!==r)if(r)t.push(r[2]);else if(/^(303|532)$/.test(a))e[a]=0;else{var d=new Promise(((t,d)=>r=e[a]=[t,d]));t.push(r[2]=d);var f=o.p+o.u(a),c=new Error;o.l(f,(t=>{if(o.o(e,a)&&(0!==(r=e[a])&&(e[a]=void 0),r)){var d=t&&("load"===t.type?"missing":t.type),f=t&&t.target&&t.target.src;c.message="Loading chunk "+a+" failed.\n("+d+": "+f+")",c.name="ChunkLoadError",c.type=d,c.request=f,r[1](c)}}),"chunk-"+a,a)}},o.O.j=a=>0===e[a];var a=(a,t)=>{var r,d,f=t[0],c=t[1],n=t[2],b=0;if(f.some((a=>0!==e[a]))){for(r in c)o.o(c,r)&&(o.m[r]=c[r]);if(n)var i=n(o)}for(a&&a(t);b{"use strict";var e,a,t,r,d,f={},c={};function o(e){var a=c[e];if(void 0!==a)return a.exports;var t=c[e]={id:e,loaded:!1,exports:{}};return f[e].call(t.exports,t,t.exports,o),t.loaded=!0,t.exports}o.m=f,o.c=c,e=[],o.O=(a,t,r,d)=>{if(!t){var f=1/0;for(i=0;i=d)&&Object.keys(o.O).every((e=>o.O[e](t[n])))?t.splice(n--,1):(c=!1,d0&&e[i-1][2]>d;i--)e[i]=e[i-1];e[i]=[t,r,d]},o.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return o.d(a,{a:a}),a},t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,o.t=function(e,r){if(1&r&&(e=this(e)),8&r)return e;if("object"==typeof e&&e){if(4&r&&e.__esModule)return e;if(16&r&&"function"==typeof e.then)return e}var d=Object.create(null);o.r(d);var f={};a=a||[null,t({}),t([]),t(t)];for(var c=2&r&&e;"object"==typeof c&&!~a.indexOf(c);c=t(c))Object.getOwnPropertyNames(c).forEach((a=>f[a]=()=>e[a]));return f.default=()=>e,o.d(d,f),d},o.d=(e,a)=>{for(var t in a)o.o(a,t)&&!o.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:a[t]})},o.f={},o.e=e=>Promise.all(Object.keys(o.f).reduce(((a,t)=>(o.f[t](e,a),a)),[])),o.u=e=>"assets/js/"+({34:"5b0e0e08",35:"b41febc4",53:"935f2afb",84:"9dc53199",85:"1f391b9e",109:"c3a18047",151:"6256d5c3",153:"97ffe59f",169:"c7a80478",172:"978828ab",222:"08f30e7d",231:"84071e39",236:"e09d216d",237:"1df93b7f",299:"33c22581",485:"ad512b16",493:"deda2cf3",502:"1178bc06",514:"1be78505",549:"f304a758",585:"a9298e25",609:"8d76850e",613:"f6536843",662:"7921cf10",687:"308aca62",732:"a7fdbc71",774:"4af54caf",777:"48e94e84",778:"e55f1e0e",817:"2b95ca9d",825:"a765e1a2",873:"67c026b6",876:"4d9d995f",885:"8d0041db",896:"08154ee9",900:"1ae5d93b",916:"e9221267",918:"17896441",920:"1a4e3797",945:"c9ae8b2d"}[e]||e)+"."+{34:"975d4ae9",35:"fb3c65a7",53:"b5055a79",84:"13b40e3c",85:"48bd894a",109:"9ecd08cc",151:"49478c67",153:"88cff6ef",169:"a02425b0",172:"1ad8f25b",222:"ad0ac244",231:"321abdbd",236:"5c109b9d",237:"52a1d67f",299:"de5322e1",485:"2de7ded0",493:"6f2a713d",502:"04a79afb",514:"51b45561",530:"f5cc4ef9",549:"756c8e8b",585:"a535df35",609:"662cf357",613:"20f6a121",638:"b54116d7",654:"d636b64d",662:"aaddc73d",687:"b045aa80",732:"8c5cb0f4",774:"0b1ea9c3",777:"57154658",778:"1e3aaf8e",817:"d3b76552",825:"b6852205",873:"8e6669d1",876:"aa00eebb",885:"2d0bbcd6",896:"011f7e3f",900:"85fac64d",901:"5fd7132c",916:"8faa3552",918:"98c8b99a",920:"67205d40",945:"e7e15d92",967:"d43ac743"}[e]+".js",o.miniCssF=e=>{},o.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),o.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),r={},d="website:",o.l=(e,a,t,f)=>{if(r[e])r[e].push(a);else{var c,n;if(void 0!==t)for(var b=document.getElementsByTagName("script"),i=0;i{c.onerror=c.onload=null,clearTimeout(s);var d=r[e];if(delete r[e],c.parentNode&&c.parentNode.removeChild(c),d&&d.forEach((e=>e(t))),a)return a(t)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:c}),12e4);c.onerror=l.bind(null,c.onerror),c.onload=l.bind(null,c.onload),n&&document.head.appendChild(c)}},o.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.p="/",o.gca=function(e){return e={17896441:"918","5b0e0e08":"34",b41febc4:"35","935f2afb":"53","9dc53199":"84","1f391b9e":"85",c3a18047:"109","6256d5c3":"151","97ffe59f":"153",c7a80478:"169","978828ab":"172","08f30e7d":"222","84071e39":"231",e09d216d:"236","1df93b7f":"237","33c22581":"299",ad512b16:"485",deda2cf3:"493","1178bc06":"502","1be78505":"514",f304a758:"549",a9298e25:"585","8d76850e":"609",f6536843:"613","7921cf10":"662","308aca62":"687",a7fdbc71:"732","4af54caf":"774","48e94e84":"777",e55f1e0e:"778","2b95ca9d":"817",a765e1a2:"825","67c026b6":"873","4d9d995f":"876","8d0041db":"885","08154ee9":"896","1ae5d93b":"900",e9221267:"916","1a4e3797":"920",c9ae8b2d:"945"}[e]||e,o.p+o.u(e)},(()=>{var e={303:0,532:0};o.f.j=(a,t)=>{var r=o.o(e,a)?e[a]:void 0;if(0!==r)if(r)t.push(r[2]);else if(/^(303|532)$/.test(a))e[a]=0;else{var d=new Promise(((t,d)=>r=e[a]=[t,d]));t.push(r[2]=d);var f=o.p+o.u(a),c=new Error;o.l(f,(t=>{if(o.o(e,a)&&(0!==(r=e[a])&&(e[a]=void 0),r)){var d=t&&("load"===t.type?"missing":t.type),f=t&&t.target&&t.target.src;c.message="Loading chunk "+a+" failed.\n("+d+": "+f+")",c.name="ChunkLoadError",c.type=d,c.request=f,r[1](c)}}),"chunk-"+a,a)}},o.O.j=a=>0===e[a];var a=(a,t)=>{var r,d,f=t[0],c=t[1],n=t[2],b=0;if(f.some((a=>0!==e[a]))){for(r in c)o.o(c,r)&&(o.m[r]=c[r]);if(n)var i=n(o)}for(a&&a(t);b Installation | effector patronum - +

Installation

yarn add patronum
# or
npm add patronum
# or
pnpm add patronum

Usage

Just import method from the root module:

import { throttle, splitMap } from 'patronum';

Or from the personal module:

import { throttle } from 'patronum/throttle';
import { splitMap } from 'patronum/split-map';

Except patronum/debug. It is only available by personal module import.

Be careful in the module naming:

  • Method export always exports as named export in camelCase
  • Module with the operator always named in param-case

Tests and SSR support

At the moment patronum supports only Babel.

Just add patronum/babel-preset to your .babelrc or babel.config.js at the "presets" section:

{
"presets": ["patronum/babel-preset"]
}

Logger and CRA support with macros

babel-plugin-macros is bundled into CRA, so we can use it due CRA don't support adding babel plugins into .babelrc or babel.config.js.

Just import from patronum/macro and effector-logger/macro, and use as early:

import { createStore, createEffect, sample } from 'effector-logger/macro';
import { status, splitMap, combineEvents } from 'patronum/macro';
  • Warning: babel-plugin-macros do not support import * as name!
  • Note: Since release of patronum v2.0.0 it is required to use babel-plugin-macros v3.0.0 or higher.
  • Please note, that react-scripts v4.0.3 and older uses outdated version of this plugin - you can either use yarn resolutions or use react-scripts v5.0.0 or higher.
- + \ No newline at end of file diff --git a/docs/migration-guide/index.html b/docs/migration-guide/index.html index df6ae167..6649d5af 100644 --- a/docs/migration-guide/index.html +++ b/docs/migration-guide/index.html @@ -4,14 +4,14 @@ Migration guide | effector patronum - +

Migration guide

v1.0.0

Removed support of effector v21. Now the minimum supported version is v22.1.2. No breaking changes in code.

v0.110

From v0.110.0 patronum removed support of effector v20. Now minimum supported version is v21.4.

Please, before upgrade review release notes of effector v21.

v0.100

From v0.100.0 patronum introduced object arguments form with BREAKING CHANGES.

npm install patronum@0.14.3
# or
yarn add patronum@0.14.3
throttle
import { throttle } from 'patronum/throttle';

Previous version v0.14.x

const throttled = throttle(source, timeout);

Current version v0.102.x

const throttled = throttle({ source, timeout });

// Also you can set target
const throttled2 = createEvent(); // or any unit
throttle({ source, timeout, target: throttled2 });
  • Wrap source and timeout arguments to object
  • Optionally add target parameter

https://github.com/sergeysova/patronum/pull/31

debounce
import { debounce } from 'patronum/debounce';

Previous version v0.14.x

const debounced = debounce(source, timeout);

Current version v0.102.x

const debounced = debounce({ source, timeout });

// Also you can set target
const debounced = createEvent(); // or any unit
debounce({ source, timeout, target: debounced });
  • Wrap source and timeout arguments to object
  • Optionally add target parameter

https://github.com/sergeysova/patronum/pull/38

splitMap
import { splitMap } from 'patronum/split-map';

Previous version v0.14.x

const received = splitMap(nameReceived, {
firstName: (string) => string.split(' ')[0], // string | undefined
lastName: (string) => string.split(' ')[1], // string | undefined
});

Current version v0.102.x

const received = splitMap({
source: nameReceived,
cases: {
firstName: (string) => string.split(' ')[0], // string | undefined
lastName: (string) => string.split(' ')[1], // string | undefined
},
});
  • First argument should be source
  • Second argument should be cases

https://github.com/sergeysova/patronum/pull/41

some
import { some } from 'patronum/some';

Previous version v0.14.x

const $tooBig = some((size) => size > 800, [$width, $height]);

Current version v0.102.x

const $tooBig = some({
predicate: (size) => size > 800,
stores: [$width, $height],
});
  • First argument should be predicate
  • Second argument should be stores

https://github.com/sergeysova/patronum/pull/43

every
import { every } from 'patronum/every';

Previous version v0.14.x

const $result = every(true, [$a, $b, $c]);
const $result = every(() => true, [$a, $b, $c]);

Current version v0.102.x

const $result = every({ predicate: true, stores: [$a, $b, $c] });
const $result = every({ predicate: () => true, stores: [$a, $b, $c] });
  • First argument should be predicate
  • Second argument should be stores

https://github.com/sergeysova/patronum/pull/50

delay
import { delay } from 'patronum/delay';

Previous version v0.14.x

const delayed = delay(unit, 100);
const logDelayed = delay(unit, { time: (payload) => 100 });

Current version v0.102.x

const delayed = delay({
source: unit,
timeout: 100,
});

const delayed = delay({
source: unit,
timeout: (payload) => 100,
});

const delayed = delay({
source: unit,
timeout: $timeout,
});
  • First argument should be source
  • Second argument should be timeout
  • time property from second argument should be timeout
  • timeout can be Store<number>

https://github.com/sergeysova/patronum/pull/51

status
import { status } from 'patronum/status';

Previous version v0.14.x

const $status = status(effect, 'initial');

Current version v0.102.x

const $status = status({ effect, defaultValue: 'initial' });
  • First argument should be effect in object
  • Second argument should be defaultValue and can be optional

https://github.com/sergeysova/patronum/pull/55

reshape
import { reshape } from 'patronum/reshape';

Previous version v0.14.x

const parts = reshape($original, {
length: (string) => string.length,
first: (string) => string.split(' ')[0] || '',
second: (string) => string.split(' ')[1] || '',
});

Current version v0.102.x

const parts = reshape({
source: $original,
shape: {
length: (string) => string.length,
first: (string) => string.split(' ')[0] || '',
second: (string) => string.split(' ')[1] || '',
},
});
  • First argument should be source
  • Second argument should be shape

https://github.com/sergeysova/patronum/pull/57

combineEvents
import { combineEvents } from 'patronum/combine-events';

Previous version v0.14.x

const target = combineEvents([first, second, third]);
const target = combineEvents({
key1: event1,
key2: event2,
});

Current version v0.102.x

const target = combineEvents({ events: [first, second, third] });
const target = combineEvents({
events: {
key1: event1,
key2: event2,
},
});
  • Assign first argument to property events in object

https://github.com/sergeysova/patronum/pull/58

spread
import { spread } from 'patronum/spread';

Previous version v0.14.x

spread(formReceived, {
first: $first,
last: $last,
});

const source = spread({
first: $first,
last: $last,
});

Current version v0.102.x

spread({
source: formReceived,
targets: {
first: $first,
last: $last,
},
});

const source = spread({
targets: {
first: $first,
last: $last,
},
});
  1. If you have two arguments:
  • First argument should be source in object
  • Second argument should be targets
  1. If only one argument:
  • Wrap it to object and assign to targets

https://github.com/sergeysova/patronum/pull/61

- + \ No newline at end of file diff --git a/index.html b/index.html index 65acb36e..50fece14 100644 --- a/index.html +++ b/index.html @@ -4,13 +4,13 @@ Welcome | effector patronum - + - + \ No newline at end of file diff --git a/methods/and/index.html b/methods/and/index.html index 40a533df..220e6abf 100644 --- a/methods/and/index.html +++ b/methods/and/index.html @@ -4,14 +4,14 @@ and | effector patronum - +

and

since

patronum 1.11.0

import { and } from 'patronum';
// or
import { and } from 'patronum/and';

Motivation

The method allows to check each passed store for thruthy values. It can be compared with if (a && b && c && d).

Formulae

$result = and(...stores);
  • $result store contains true if each passed store contains "truthy" values.

Arguments

The method receives any amount of arguments.

  • ...stores: Array<Store<T>> — Each argument must be store with a value of any kind.

Returns

  • $result: Store<boolean> — The store contains false if at least one passed store in stores contains "falsey" value

Example

const $isAuthorized = createStore(true);
const $isAdmin = createStore(false);
const $orderFinished = createStore(true);

const $showCancelButton = and($isAuthorized, $isAdmin, $orderFinished);
console.assert(false === $showCancelButton.getState());

Try it

Composition

const $showRegisterLink = and(
not($isAuthorized),
or($isRegisterInProcess, $isEmailRecovering),
);

Alternative

const $showRegisterLink = combine(
$isAuthorized,
$isRegisterInProcess,
$isEmailRecovering,
(isAuthorized, isRegisterInProcess, isEmailRecovering) => {
return !isAuthorized && (isRegisterInProcess || isEmailRecovering);
}
);
console.assert(false === $showRegisterLink.getState());
- + \ No newline at end of file diff --git a/methods/combine-events/index.html b/methods/combine-events/index.html index 37d65b2f..dfdb64b2 100644 --- a/methods/combine-events/index.html +++ b/methods/combine-events/index.html @@ -4,14 +4,14 @@ combineEvents | effector patronum - +

combineEvents

import { combineEvents } from 'patronum';
// or
import { combineEvents } from 'patronum/combine-events';

combineEvents(events)

since

patronum 2.1.0 Use combineEvents({ events }) with patronum < 2.1.0

Motivation

Method allows to trigger event when all of given events are triggered, with payloads ov given events

note

Consider using stores with combine in case of lazy-loaded modules, as they could miss some updates happened before module loaded

Formulae

const target = combineEvents({ key1: event1, key2: event2 });
  • When all events are triggered, trigger target with {key1: firstPayload, key2: secondPayload}
const target = combineEvents([event1, event2]);
  • When all events are triggered, trigger target with [firstPayload, secondPayload]

Arguments

  1. events — Object or array with events

Returns

  • target — Event with the same shape as events, that triggered after all events triggered

combineEvents({ events, reset, target })

Motivation

Object form which allow to pass reset unit or target

Formulae

const target = combineEvents({
events: {
key1: event1,
key2: event2,
},
reset: resetUnit,
target: targetUnit,
});
  • When all events are triggered, trigger target with {key1: firstPayload, key2: secondPayload}
const target = combineEvents({
events: [event1, event2],
reset: resetUnit,
target: targetUnit,
});
  • When all events are triggered, trigger target with [firstPayload, secondPayload]

Arguments

  1. events — Object or array with events
  2. reset (Unit<any>) - Optional. Any unit which will reset state of combineEvents and collecting of payloads will start from scratch
  3. target (Unit<Shape>) - Optional. Any unit with type matching events shape

Returns

  • target — When target option is not defined, will return new event with the same shape as events, otherwise target unit will returns

Example

const first = createEvent<number>();
const second = createEvent<string>();
const third = createEvent<boolean>();
const target = createEvent<{ a: number; b: string; c: boolean }>();
const reset = createEvent();

combineEvents({
events: {
a: first,
b: second,
c: third,
},
reset,
target,
});

target.watch((object) => {
console.log('first event data', object.a);
console.log('second event data', object.b);
console.log('third event data', object.c);
});

first(15); // nothing
second('wow'); // nothing
third(false); // target triggered with {a: 15, b: 'wow', c: false}

first(10);
second('-');

reset(); // combineEvents state is erased

third(true); // nothing, as it's a first saved payload
first(0);
second('ok'); // target triggered with {a: 0, b: 'ok', c: true}
- + \ No newline at end of file diff --git a/methods/condition/index.html b/methods/condition/index.html index 2fe34cde..311b6c87 100644 --- a/methods/condition/index.html +++ b/methods/condition/index.html @@ -4,13 +4,13 @@ condition | effector patronum - +

condition

import { condition } from 'patronum';
// or
import { condition } from 'patronum/condition';

Motivation

Condition is very similar to sample, but allows you to have else branch along with simple if matcher.

condition({ source: Unit, if: Store, then?: Unit, else?: Unit })

Formulae

result = condition({
source,
if: $checker,
then,
else,
});
  • When source is triggered, check value of $checker, if it equals true, trigger then with value from source, otherwise trigger else with value from source
  • result is the same unit as source allows to nest condition to another condition or sample

Arguments

  1. source (Unit<T>) — Data from this unit will be passed to then or else
  2. if (Store<boolean>) — Updates of this store will not trigger then and else
  3. then (Unit<T>) — This unit will be triggered with data from source if $checker contains true. Required if else is not provided
  4. else (Unit<T>) — This unit will be triggered with data from source if $checker contains false. Required if then is not provided

Returns

  1. (Unit<T>) — The same unit type that passed to source

Example

const change = createEvent();
const $source = createStore('data').on(change, (_, payload) => payload);

const toggle = createEvent();
const $isEnabled = createStore(false).on(toggle, (is) => !is);

const enabled = createEvent();
const disabled = createEvent();

condition({
source: $source,
if: $isEnabled,
then: enabled,
else: disabled,
});
enabled.watch((payload) => console.log('enabled -', payload));
disabled.watch((payload) => console.log('disabled -', payload));

change('newdata');
// => disabled - newdata

toggle();
change('data');
// => enabled - data

condition({ source: Unit<T>, if: T, then?: Unit, else?: Unit })

Formulae

result = condition({
source,
if: value,
then,
else,
});
  • When source is triggered, compare value literal with source payload, if it equals trigger then with value from source, otherwise trigger else with value from source
  • result is the same unit as source allows to nest condition to another condition or sample

Arguments

  1. source (Unit<T>) — Data from this unit will be passed to then or else
  2. if (T) — Just value to compare with source payload. Note: objects will be compared by reference
  3. then (Unit<T>) — This unit will be triggered with data from source if $checker contains true. Required if else is not provided
  4. else (Unit<T>) — This unit will be triggered with data from source if $checker contains false. Required if then is not provided

Returns

  1. (Unit<T>) — The same unit type that passed to source

Example

const increment = createEvent();
const $source = createStore(0).on(increment, (state) => state + 1);

const log = createEvent();
const run = createEffect().use((data) => {
console.info('FAKE RUN EFFECT', data);
});

condition({
source: $source,
if: 4,
then: run,
else: log,
});

log.watch((payload) => console.log('LOG ABOUT IT', payload));

increment(); // => LOG ABOUT IT 1
increment(); // => LOG ABOUT IT 2
increment(); // => LOG ABOUT IT 3
increment(); // => FAKE RUN EFFECT 4
increment(); // => LOG ABOUT IT 5

condition({ source: Unit<T>, if: Function, then?: Unit, else?: Unit })

Formulae

result = condition({
source,
if: (payload) => boolean,
then,
else,
});
  • When source is triggered, call if with source payload, if it returns true trigger then with value from source, otherwise trigger else with value from source
  • result is the same unit as source allows to nest condition to another condition or sample

Arguments

  1. source (Unit<T>) — Data from this unit will be passed to then or else
  2. if ((payload: T) => boolean) — Function comparator. It should return boolean
  3. then (Unit<T>) — This unit will be triggered with data from source if $checker contains true. Required if else is not provided
  4. else (Unit<T>) — This unit will be triggered with data from source if $checker contains false. Required if then is not provided

Returns

  1. (Unit<T>) — The same unit type that passed to source

Example

const change = createEvent();
const $source = createStore('data').on(change, (_, payload) => payload);
const target = createEvent();
const another = createEvent();

condition({
source: $source,
if: (source) => source.length > 3,
then: target,
else: another,
});
target.watch((payload) => console.log('triggered', payload));
another.watch((payload) => console.log('condition else:', payload));

change('newdata');
// => triggered newdata

change('old');
// => condition else: old

Examples

Source is event

const inputChanged = createEvent();
const $value = createStore('');
const $error = createStore(false);

const setError = createEvent();
const setValue = createEvent();

$value.on(setValue, (_, value) => value);
$error.on(setError, () => true).on(setValue, () => false);

condition({
source: inputChanged,
if: isValid,
then: setValue,
else: setError,
});

function isValid(value) {
return value.trim().length > 0;
}

Condition can be nested

const $value = createStore('hello@world');
const updateEmail = createEvent<string>();

condition({
source: $value,
if: (length) => length > 0,
then: condition({
if: (string) => string.includes('@'),
then: updateEmail,
}),
});
- + \ No newline at end of file diff --git a/methods/debounce/index.html b/methods/debounce/index.html index a18ee7e4..a5078f44 100644 --- a/methods/debounce/index.html +++ b/methods/debounce/index.html @@ -4,7 +4,7 @@ debounce | effector patronum - + @@ -13,7 +13,7 @@ It is useful when you want to pass created event immediately to another method as argument.

since

patronum 2.1.0 Use debounce({ source, timeout }) with patronum < 2.1.0

Formulae

event = debounce(source, timeout);
  • Wait for timeout after the last time source was triggered, then trigger event with payload of the source

Arguments

  1. source (Event<T> | Store<T> | Effect<T>) — Source unit, data from this unit used by the event
  2. timeout (number | Store<number>) — time to wait before trigger event

Returns

  • event (Event<T>) — New event, that triggered after delay

Example

import { createEvent } from 'effector';
import { debounce } from 'patronum/debounce';

const DEBOUNCE_TIMEOUT_IN_MS = 200;

const someHappened = createEvent<number>();
const debounced = debounce(someHappened, DEBOUNCE_TIMEOUT_IN_MS);

debounced.watch((payload) => {
console.info('someHappened now', payload);
});

someHappened(1);
someHappened(2);
someHappened(3);
someHappened(4);

// someHappened now 4

Example with timeout as store

import { createStore } from 'effector';
import { debounce } from 'patronum';

const DEBOUNCE_TIMEOUT_IN_MS = 200;

const changeTimeout = createEvent<number>();
const $timeout = createStore(DEBOUNCE_TIMEOUT_IN_MS).on(
changeTimeout,
(_, value) => value,
);
const someHappened = createEvent<number>();
const debounced = debounce(someHappened, $timeout);

debounced.watch((payload) => {
console.info('someHappened now', payload);
});

someHappened(1);
changeTimeout(400); // will be applied after next source trigger
someHappened(2);

setTimeout(() => {
// console clear
}, 200);

setTimeout(() => {
// someHappened now 2
}, 400);

debounce({ source, timeout, target })

Motivation

This overload receives target as argument, that will be triggered after timeout. It is useful when you already have an unit that you need to trigger.

Formulae

event = debounce({ source, timeout, target });
  • Wait for timeout after the last time source was triggered and call target with data from the source

Arguments

  1. source (Event<T> | Store<T> | Effect<T>) — Source unit, data from this unit used to trigger target with payload of the source
  2. timeout (number | Store<number>) — time to wait before trigger event
  3. target (Event<T> | Store<T> | Effect<T>) — Target unit, data from the source will be passed to this unit

Returns

  • target (Event<T> | Store<T> | Effect<T>) — Target unit that was passed to input argument target

Example

import { createEvent, createStore } from 'effector';
import { debounce } from 'patronum/debounce';

const DEBOUNCE_TIMEOUT_IN_MS = 200;

const someHappened = createEvent<number>();
const target = createStore<number>(0);
const debounced = debounce({
source: someHappened,
timeout: DEBOUNCE_TIMEOUT_IN_MS,
target,
});

debounced.watch((payload) => {
console.info('someHappened now', payload);
});

target.watch((payload) => {
console.info('got data', payload);
});

someHappened(1);
someHappened(2);
someHappened(3);
someHappened(4);

// someHappened now 4
// got data 4

debounce({ source, timeout })

Motivation

This overload recieves source and timeout as an object. May be useful for additional clarity, but it's longer to write

Formulae

event = debounce({ source, timeout });
  • Wait for timeout after the last time source was triggered, then trigger event with payload of the source

Arguments

  1. source (Event<T> | Store<T> | Effect<T>) — Source unit, data from this unit used by the event
  2. timeout (number | Store<number>) — time to wait before trigger event

Returns

  • event (Event<T>) — New event, that triggered after delay

Example

import { createEvent } from 'effector';
import { debounce } from 'patronum/debounce';

const DEBOUNCE_TIMEOUT_IN_MS = 200;

const someHappened = createEvent<number>();
const debounced = debounce({
source: someHappened,
timeout: DEBOUNCE_TIMEOUT_IN_MS,
});

debounced.watch((payload) => {
console.info('someHappened now', payload);
});

someHappened(1);
someHappened(2);
someHappened(3);
someHappened(4);

// someHappened now 4
- + \ No newline at end of file diff --git a/methods/debug/index.html b/methods/debug/index.html index 4acde700..44059e8b 100644 --- a/methods/debug/index.html +++ b/methods/debug/index.html @@ -4,7 +4,7 @@ debug | effector patronum - + @@ -13,7 +13,7 @@ It is recommended to use this feature along with effector/babel-plugin.

const inputChanged = createEvent();
const $form = createStore(0).on(inputChanged, (s) => s + 1);

debug({ trace: true }, $form, submitFx);

inputChanged();

// "[store] $form 0",
// "[store] $form 1",
// "[store] $form trace",
// "<- [store] $form 1",
// "<- [$form.on] $form.on(inputChanged) 1",
// "<- [event] inputChanged ",

Custom names

Sometimes unit name in specific context may be different from the one it was initially created with. e.g., an unit may be exported under an alias for explicitness:

export const $productsListVisible = productsPageModel.$open;
const productAdded = createEvent();

debug($productsListVisible, productAdded);
// or
debug({ trace: true }, $productsListVisible, productAdded);

In this case, because of effector/babel-plugin which provided productsPageModel.$open store its name at the moment of its creation, public name in the debug logs will be $open instead of $productsListVisible.

It can be fixed with custom name, which can be provided by using Record<string, Unit> instead of a list of units:

export const $productsListVisible = productsPageModel.$open;
const productAdded = createEvent();

debug({ $productsListVisible, customEventName: productAdded });
// or
debug({ trace: true }, { $productsListVisible, customEventName: productAdded });

This way $productsListVisible name in the logs will be the same, as the one which was provided to debug. The productAdded event will be named customEventName.

Fork API and Scope

Effector can run multiple "instances" of the app simultaniosly via Fork API - it is useful for tests and SSR. Usually you would also use scope on the client in the case of SSR. debug will log "scoped" updates in such case:

const up = createEvent();
const $count = createStore(0).on(up, (s) => s + 1);
const fx = createEffect(() => {});
sample({
clock: $count,
target: fx,
});

debug(fx);

const scopeA = fork();

await allSettled(up, { scope: scopeA });

// "[effect] (scope: unknown_scope_3) fx 1",
// "[effect] (scope: unknown_scope_3) fx.done {\\"params\\":1}",

By default detected scope will be given default name.

Scope registration

It is possible to explicitly register scope with given name to have a more explicit logs.

It can work like this:

const scope = fork({ values: window.__SSR_VALUES__ });

// this way we can commit this to our repo and be sure,
// that bundler will cut this out of production bundle
if (process.env.NODE_ENV === 'development') {
debug.registerScope(scope, { name: 'my_client_scope' });
}

This way scope will be given explicit name in the logs.

// "[effect] (scope: my_client_scope) fx 1",
// "[effect] (scope: my_client_scope) fx.done {\\"params\\":1}",

It is also possible to unregister scope to prevent memory leak, if scope is no longer needed:

const unregister = debug.registerScope(scope, { name: `my_scope` });

unregister();

Or unregister all scopes at once:

debug.unregisterAllScopes();

Initial store state

debug($store) always immediatly prints current state of the store, but this state can be different in different scopes. It is recommened to register scopes explicitly, since debug will print current state of the store in every known scope:

const $count = createStore(0);

const scopeA = fork({
values: [[$count, 42]],
});
const scopeB = fork({
values: [[$count, 1337]],
});

debug.registerScope(scopeA, { name: 'scope_42' });
debug.registerScope(scopeB, { name: 'scope_1337' });

debug($count);

// "[store] $count 0",
// "[store] (scope: scope_42) $count 42",
// "[store] (scope: scope_1337) $count 1337",

Customization

The patronum/debug package extracts quite a lot of low-level effector data in a universal format that may be useful for other dev tools or monitoring tools, so there is a special API to add your own way of handling this data.

Custom handler

You can provide custom log handler in the config. It can be useful, if console.info somehow doesn't fit your use-case: e.g. you want advanced info from patronum/debug to built your own dev-tools, debug especially hard case, etc.

Handler is called for each update with context object like this:

Common fields:

  • logType - initial | update - log type for convenience. All provided stores current values are logged with initial for all known scopes right away. Same after new scope was registered. All other updates are of update type.
  • scope - Scope | null - effector's Scope context object, which owns this particular update
  • scopeName - string | null - name of the Scope, if registered and null otherwise.
  • node - Node - effector's internal node, which update is being logged.
  • name - string | null - node's name for convenience. null - if node doesn't have own name (like sample calls).
  • kind - string - node's kind for convenience. It can be unit's kind (e.g. store or event) or operation kind (e.g. sample, split, etc).
  • value - unknown - value of the update.
  • loc - { file?: string; line: number; column: number; } - location in the source code, if known
  • stackMeta - { Record<string, unknown> } - effector's internal stack metadata, available if provided. For example, effect calls provide an fxID that is stable between fx and fx.finally updates - this allows you to associate a fx.finally update with a particular fx call (available since effector@22.5.0)

Special field if trace: true provided:

  • trace - Array<{ node: Node; name: string | null; kind: string; value: unknown; loc?: Loc; stackMeta?: Record<string, unknown> }> - trace of updates.

The trace array is always empty (i.e. trace is not collected), if debug's config does not have trace: true.

debug(
{
trace: true,
// custom log handler
handler: ({ trace, scope, scopeName, node, value, name, kind }) => {
// your own way to log updates
doStuff(node, value);

if (trace) {
// log trace part
trace.forEach(({ name, kind, node, value }) => doStuff(node, value));
}
},
},
{ $a, $b, c },
);
- + \ No newline at end of file diff --git a/methods/delay/index.html b/methods/delay/index.html index e62ff6e0..caac1d3f 100644 --- a/methods/delay/index.html +++ b/methods/delay/index.html @@ -4,7 +4,7 @@ delay | effector patronum - + @@ -17,7 +17,7 @@ Use delay({ source, timeout }) with patronum < 2.1.0

Motivation

This overload allows you to read timeout from another store. It is useful when you writing music editor and need dynamic delay for your events.

Formulae

target = delay(source, timeout);
  • When source is triggered, read timeout from timeout store, then trigger target with payload of the source

Arguments

  1. source (Event<T> | Store<T> | Effect<T>) — Source unit, data from this unit used to trigger target with.
  2. timeout (Store<number>) — Store with number — delay in milliseconds.

Returns

  • target (Event<T>) — New event which will receive source payload after timeout delay

Example

import { createEvent, createStore } from 'effector';
import { delay } from 'patronum/delay';

const update = createEvent<string>();
const $timeout = createStore(500);

const logDelayed = delay(update, $timeout);

logDelayed.watch((data) => console.log('log', data));

update('Hello');
// after 500ms
// => log Hello

delay({ source, timeout: Store<T>, target })

Motivation

This overload allows you to read timeout from another store. It is useful when you writing music editor and need dynamic delay for your events.

Formulae

target = delay({ source, timeout: $store, target });
  • When source is triggered, read timeout from timeout store, then trigger target with payload of the source

Arguments

  1. source (Event<T> | Store<T> | Effect<T>) — Source unit, data from this unit used to trigger target with.
  2. timeout (Store<number>) — Store with number — delay in milliseconds.
  3. target (Unit<T> | Array<Unit<T>>) — Optional. Target unit or array of units that will be called after delay.

Returns

  • target (Unit<T> | Array<Unit<T>>) — Target unit or units that were passed to delay

Example

import { createEvent, createStore } from 'effector';
import { delay } from 'patronum/delay';

const update = createEvent<string>();
const $timeout = createStore(500);

delay({
source: update,
timeout: $timeout,
target: logDelayed,
});

logDelayed.watch((data) => console.log('log', data));

update('Hello');
// after 500ms
// => log Hello
- + \ No newline at end of file diff --git a/methods/either/index.html b/methods/either/index.html index 1fc33d1a..eef5a5f4 100644 --- a/methods/either/index.html +++ b/methods/either/index.html @@ -4,14 +4,14 @@ either | effector patronum - +

either

since

patronum 1.11.0

import { either } from 'patronum';
// or
import { either } from 'patronum/either';

Motivation

The method select one or other value based on condition. You can think about it as a ternary operator a ? b : c.

Formulae

$result = either($filter, then, other);
  • $result store contains value of then if $filter store contains true
  • $result store contains value of other if $filter store contains false

Arguments

  1. $filter: Store<boolean> — The store contains condition how to select between then and other
  2. then: Store<Then> | Then — First value can be a Store, and returned when $filter is true
  3. other: Store<Other> | Other — Second value can be a Store too, returned only when $filter if false
  • Then — Generic type argument. Required only to distinguish this type from Other
  • Other — Generic type argument used for the alternative value when $filter is false

Returns

  • $result: Store<Then | Other> — Store contains one of the value depends on $filter value

Object form arguments

For some cases it is useful to add names for each argument.

$result = either({
filter,
then,
other,
});

Types, descriptions, and usage the same as for positional arguments.

Example

const $showLatest = createStore(false);
const $latestTweets = createStore<Tweet[]>([]);
const $recommendedTweets = createStore<Tweet[]>([]);

export const $tweets = either($showLatest, $latestTweets, $recommendedTweets);

Try it

Select just one argument

Sometimes we want to write an inverted expression, like this imperative code:

if (!active) {
return another;
}
return null;

We can write exactly null as is:

const $result = either($active, null, $another); // Store<null | Another>

We may want to use not for condition:

import { either, not } from 'patronum';

const $result = either(not($active), $another, null);

Alternative

import { createStore, combine } from 'effector';
const $showLatest = createStore(false);
const $latestTweets = createStore<Tweet[]>([]);
const $recommendedTweets = createStore<Tweet[]>([]);

export const $tweets = combine(
$showLatest,
$latestTweets,
$recommendedTweets,
(showLatest, latestTweets, recommendedTweets) =>
showLatest ? latestTweets : recommendedTweets,
);
- + \ No newline at end of file diff --git a/methods/empty/index.html b/methods/empty/index.html index c9aff1a2..efb6f0fd 100644 --- a/methods/empty/index.html +++ b/methods/empty/index.html @@ -4,14 +4,14 @@ empty (experimental) | effector patronum - +

empty (experimental)

since

patronum 1.10.0

experimental

Operator is going to be renamed. Please review the discussion.

import { empty } from 'patronum';
// or
import { empty } from 'patronum/empty';

Motivation

The method allows to check passed value for null. Sometimes you need just to check for nullability, and go on.

Formulae

$result = empty($value);
  • $result store contains true if $value store contains null

Arguments

  • $value: Store<T | null> — The store contains any kind of value.

Returns

  • $result: Store<boolean> — The store contains false if passed $value store contains any kind of value other from null

Example

const $account = createStore<Account | null>(null);
const $anonymous = empty($account);
const $authorized = not($anonymous);

console.assert(true === $anonymous.getState());
console.assert(false === $authorized.getState());

Try it

Alternative

import { createStore } from 'effector';
const $account = createStore<Account | null>(null);
const $anonymous = $account.map((account) => account === null);
const $authorized = $anonymous.map((anonymous) => !anonymous);

console.assert(true === $anonymous.getState());
console.assert(false === $authorized.getState());
- + \ No newline at end of file diff --git a/methods/equals/index.html b/methods/equals/index.html index ce5d348f..94614379 100644 --- a/methods/equals/index.html +++ b/methods/equals/index.html @@ -4,13 +4,13 @@ equals | effector patronum - +

equals

since

patronum 1.11.0

import { equals } from 'patronum';
// or
import { equals } from 'patronum/equals';

Motivation

The method allows to compare store value with another value wrote as literal or contained in another store.

Formulae

$isEquals = equals(first, second);
  • $isEquals will be store with true if value in first and second is equals by comparing them with ===

Arguments

  1. first: Store<T> | T — A value or the store to compare with second
  2. second: Store<T> | T — A value or the store to be compared with first

Returns

  • $isEquals: Store<boolean> — The store contains true if values is equals

Example

const $first = createStore('foo bar');
const $isEquals = equals($first, 'foo bar');

console.assert(true === $isEquals.getState());

Try it

Composition

The equals operator can be composed with any other method of patronum:

const $account = createStore<Account | null>(null);
const $authorized = not(equals($account, null));
// $authorized contains `true` only when $account contains not `null`

Alternative

Compare to literal value:

import { createStore } from 'effector';
const $first = createStore('foo bar');
const $isEquals = $first.map((first) => first === 'foo bar');

console.assert(true === $isEquals.getState());

Compare to another store:

import { createStore, combine } from 'effector';
const $first = createStore('foo bar');
const $second = createStore('foo bar');
const $isEquals = combine($first, $second, (first, second) => first === second);

console.assert(true === $isEquals.getState());
- + \ No newline at end of file diff --git a/methods/every/index.html b/methods/every/index.html index fd3501b9..6ad00e2f 100644 --- a/methods/every/index.html +++ b/methods/every/index.html @@ -4,7 +4,7 @@ every | effector patronum - + @@ -13,7 +13,7 @@ It is useful to check that user has correct values in each state.

Formulae

$result = every({ predicate: fn, stores });
  • $result will be true if each call predicate on each store value from values returns true, otherwise it will be false

Arguments

  1. predicate: (value: T) => boolean — Function to check store value
  2. stores: Array<Store<T>> — List of stores

Return

  • $result: Store<boolean>true if each store corresponds to predicate

Example

const $width = createStore(440);
const $height = createStore(780);

const $fitsSquare = every({
predicate: (size) => size < 800,
stores: [$width, $height],
});

console.assert(true === $fitsSquare.getState());

every({ predicate: value, stores })

Motivation

This overload compares each store to specific value in predicate. It is useful when you write combine with && very often, for example to create a pending state or a form valid flag.

Formulae

$result = every({ predicate: value, stores });
  • $result will be true if each value in stores equals value, otherwise it will be false

Arguments

  1. predicate (T) — Data to compare stores values with
  2. stores (Array<Store<T>>) — List of stores to compare with value
  3. type of value and stores should be the same

Return

  • $result (Store<boolean>)true if each store contains value

Example

const $isPasswordCorrect = createStore(true);
const $isEmailCorrect = createStore(true);

const $isFormCorrect = every({
predicate: true,
stores: [$isPasswordCorrect, $isEmailCorrect],
});

console.assert(true === $isFormCorrect.getState());

every({ predicate: Store, stores })

Motivation

This overload compares each store to specific value in the store predicate. It is useful when you write combine with && very often, for example to create a pending state or a form valid flag.

Formulae

since

patronum 1.7.0

$result = every({ predicate: $value, stores });
  • $result will be true if each value in stores equals value in the $value, otherwise it will be false

Arguments

  1. predicate (Store<T>) — Store contains value to compare values from stores with
  2. stores (Array<Store<T>>) — List of stores to compare with $value store
  3. type of value and stores should be the same

Return

  • $result (Store<boolean>)true if each store contains value from the predicate store

Example

const $allowToCompare = createStore(true);

const $isPasswordCorrect = createStore(true);
const $isEmailCorrect = createStore(true);

const $isFormCorrect = every({
predicate: $allowToCompare,
stores: [$isPasswordCorrect, $isEmailCorrect],
});

console.assert(true === $isFormCorrect.getState());

Shorthands

since

patronum 1.7.0

$result = every(stores, value);
$result = every(stores, (value) => false);
$result = every(stores, $predicate);

Shorthand have the same rules as the main overrides, just it uses positional arguments instead of object-form.

Arguments

  1. stores (Array<Store<T>>) — List of stores to compare with predicate in the second argument
  2. predicate (Store<T> | (value: T) => boolean | T) — Predicate to compare with
- + \ No newline at end of file diff --git a/methods/format/index.html b/methods/format/index.html index 37b8c1a4..2486a2c8 100644 --- a/methods/format/index.html +++ b/methods/format/index.html @@ -4,14 +4,14 @@ format | effector patronum - +

format

since

patronum 1.7.0

import { format } from 'patronum';
// or
import { format } from 'patronum/format';

Motivation

Sometimes you need to combine several stores into one string, but without format you need to write boring combine with combinator-function.

Formulae

$string = format`parts${$store}`;
  • on each $store change recalculate template and update $string

Arguments

format should be called as tagged template literal function

Each part of template literal will be converted to string by String(argument), that's why format supports Store<boolean | string | number>, but also you can pass list of values, and raw values.

Returns

Method always returns Store<string>

Example with stores with strings

import { createStore } from 'effector';
import { format } from 'patronum';

const $firstName = createStore('John');
const $lastName = createStore('Doe');

const $fullName = format`${$firstName} ${$lastName}`;
$fullName.watch(console.log);
// => John Doe

Example with stores with arrays

import { createStore } from 'effector';
import { format } from 'patronum';

const $list = createStore(['First', 'Second', 'Third']);

const $string = format`We have: "${list}"`;
$string.watch(console.log);
// => We have: "First, Second, Third"
- + \ No newline at end of file diff --git a/methods/in-flight/index.html b/methods/in-flight/index.html index cb895a44..b6556521 100644 --- a/methods/in-flight/index.html +++ b/methods/in-flight/index.html @@ -4,7 +4,7 @@ inFlight | effector patronum - + @@ -13,7 +13,7 @@ Use inFlight({ effects }) with patronum < 2.1.0

Motivation

Method allows to calculate total current in flight states of each passed effect. It is useful when you want to show pending state of complex process.

Formulae

$count = inFlight([fx1, fx2]);
  • Count all pending runs of effects in one store

Arguments

  1. effects (Array<Effect<any, any, any>>) - array of any effects

Returns

  • $count (Store<number>) - Store with count of run effects in pending state

Example

import { createEffect } from 'effector';
import { inFlight } from 'patronum/in-flight';

const loadFirst = createEffect().use(() => Promise.resolve(null));
const loadSecond = createEffect().use(() => Promise.resolve(2));
const $count = inFlight([loadFirst, loadSecond]);

$count.watch((count) => console.info(`count: ${count}`));
// => count: 0

loadFirst();
loadSecond();
// => count: 2

loadSecond();
loadSecond();
// => count: 4

// Wait to resolve all effects
// => count: 0

inFlight({ domain })

Motivation

This overload allows to count effects in flight of the whole domain. It is usef when you want to show loading state of the whole application.

Formulae

$count = inFlight({ domain });
  • Count all pending runs of effects in passed domain in one store

Arguments

  1. domain (Domain) - domain to count effects from

Returns

  • $count (Store<number>) - Store with count of run effects in pending state

Example

import { createDomain } from 'effector';
import { inFlight } from 'patronum/in-flight';

const app = createDomain();
const loadFirst = app.createEffect().use(() => Promise.resolve(null));
const loadSecond = app.createEffect().use(() => Promise.resolve(2));
const $count = inFlight({ domain: app });

$count.watch((count) => console.info(`count: ${count}`));
// => count: 0

loadFirst();
loadSecond();
// => count: 2

loadSecond();
loadSecond();
// => count: 4

// Wait to resolve all effects
// => count: 0

inFlight({ effects: [] })

Motivation

This overload recieves effects as an object. May be useful for additional clarity, but it's longer to write

Formulae

$count = inFlight({ effects: [fx1, fx2] });
  • Count all pending runs of effects in one store

Arguments

  1. effects (Array<Effect<any, any, any>>) - array of any effects

Returns

  • $count (Store<number>) - Store with count of run effects in pending state

Example

import { createEffect } from 'effector';
import { inFlight } from 'patronum/in-flight';

const loadFirst = createEffect().use(() => Promise.resolve(null));
const loadSecond = createEffect().use(() => Promise.resolve(2));
const $count = inFlight({ effects: [loadFirst, loadSecond] });

$count.watch((count) => console.info(`count: ${count}`));
// => count: 0

loadFirst();
loadSecond();
// => count: 2

loadSecond();
loadSecond();
// => count: 4

// Wait to resolve all effects
// => count: 0
- + \ No newline at end of file diff --git a/methods/index.html b/methods/index.html index a4da3d0b..b3c93233 100644 --- a/methods/index.html +++ b/methods/index.html @@ -4,13 +4,13 @@ Methods | effector patronum - +

Methods

All methods split into categories.

Predicate

  • and — Checks all stores by truthy value.
  • condition — Trigger then or else by condition.
  • either — Selects just one value based on condition.
  • empty — Checks the store for null.
  • equals — Checks the store for some value.
  • every — Checks that state in each store passes the predicate test.
  • not — Inverts store boolean-value.
  • or — Checks at least one store for truthy value.
  • once — Runs only once.
  • reset — Reset all passed stores by clock.
  • some — Checks that state in at least one store passes the predicate test.

Effect

  • inFlight — Counts all pending effects
  • pending — Checks that has effects in pending state.
  • status — Return text representation of effect state.

Timeouts

  • debounce — Creates event which waits until time passes after previous trigger.
  • delay — Delays the call of the event by defined timeout.
  • interval — Creates a dynamic interval with any timeout.
  • throttle — Creates event which triggers at most once per timeout.
  • time — Allows reading current timestamp by triggering clock.

Combination/Decomposition

  • combineEvents — Wait for all passed events is triggered.
  • format — Combine stores to a string literal.
  • readonly — Create readonly version of store or event.
  • reshape — Destructure one store to different stores
  • snapshot — Create store value snapshot.
  • splitMap — Split event to different events and map data.
  • spread — Send fields from object to same targets.
  • previous - Get previous value of store.

Debug

  • debug — Log triggers of passed units.
- + \ No newline at end of file diff --git a/methods/interval/index.html b/methods/interval/index.html index 4875c28b..3ef7f5ab 100644 --- a/methods/interval/index.html +++ b/methods/interval/index.html @@ -4,7 +4,7 @@ interval | effector patronum - + @@ -17,7 +17,7 @@ will be final call of tick.

Returns

  • An object of event tick and store isRunning
    • tick (Event<void) - the event is run on each timeout ms after start trigger until stop trigger
    • isRunning (Store<boolean>) - the store contains false until start is triggered, next until stop triggered the store will be true.

Example

import { createStore, createEvent } from 'effector';
import { interval } from 'patronum';

const startCounter = createEvent();
const stopCounter = createEvent();
const $counter = createStore(0);

const { tick } = interval({
timeout: 500,
start: startCounter,
stop: stopCounter,
});

$counter.on(tick, (number) => number + 1);
$counter.watch((value) => console.log('COUNTER', value));

startCounter();

setTimeout(() => stopCounter(), 5000);

Try it

@@trigger protocol

interval supports @@trigger protocol. It means that you can use interval whatever you can use trigger with, just do not pass start and stop options.

import { interval } from 'patronum';

somethingThatRequiresTrigger({
trigger: interval({ timeout: 1000 }),
});

For example, you can use interval to refresh data in the Query from the library Farfetched using @@trigger protocol.

import { keepFresh } from '@farfetched/core';
import { interval } from 'patronum';

keepFresh(someQuery, {
// 👇 Query will be refreshed each 1000 ms
triggers: [interval({ timeout: 1000 })],
});
- + \ No newline at end of file diff --git a/methods/not/index.html b/methods/not/index.html index 4c345d41..dcd2a130 100644 --- a/methods/not/index.html +++ b/methods/not/index.html @@ -4,7 +4,7 @@ not | effector patronum - + @@ -12,7 +12,7 @@

not

since

patronum 1.11.0

import { not } from 'patronum';
// or
import { not } from 'patronum/not';

Motivation

The method allows to apply boolean NOT to a value. Actually converts any "falsey" value into true (null, 0, empty string). But undefined is not present, because Store cannot hold it inside.

Formulae

$result = not($value);
  • $result store contains false if $value contains any "truthy" value, otherwise there will be true

Arguments

  • $value: Store<T> — Any value, that required to be "inverted"

Returns

  • $result: Store<boolean>

Example

const $isFinished = createStore(false);
const $stillGoingOn = not($isFinished);

console.assert(true === $stillGoingOn.getState());

Try it

Alternative

const $isFinished = createStore(false);
const $stillGoingOn = $isFinished.map((isFinished) => !isFinished);

console.assert(true === $stillGoingOn.getState());
- + \ No newline at end of file diff --git a/methods/once/index.html b/methods/once/index.html index fe539adb..48f604e5 100644 --- a/methods/once/index.html +++ b/methods/once/index.html @@ -4,7 +4,7 @@ once | effector patronum - + @@ -12,7 +12,7 @@

once

import { once } from 'patronum';
// or
import { once } from 'patronum/once';

target = once(source)

Motivation

The method allows to do something only on the first ever trigger of source. It is useful to trigger effects or other logic only once per application's lifetime.

Formulae

target = once(source);
  • When source is triggered, launch target with data from source, but only once.

Arguments

  • source (Event<T> | Effect<T> | Store<T>) — Source unit, data from this unit is used by target.

Returns

  • target Event<T> — The event that will be triggered exactly once after source is triggered.

Example

const messageReceived = createEvent<string>();
const firstMessageReceived = once(messageReceived);

firstMessageReceived.watch((message) =>
console.log('First message received:', message),
);

messageReceived('Hello'); // First message received: Hello
messageReceived('World');

Alternative

import { createGate } from 'effector-react';

const PageGate = createGate();

sample({
source: once(PageGate.open),
target: fetchDataFx,
});

target = once({ source, reset })

Motivation

This overload may receive reset in addition to source. If reset is fired, target will be allowed to trigger one more time, when source is called.

Formulae

target = once({ source, reset });
  • When source is triggered, launch target with data from source, but only once.
  • When reset is triggered, let once be reset to the initial state and allow target to be triggered again upon source being called.

Arguments

  • source (Event<T> | Effect<T> | Store<T>) — Source unit, data from this unit is used by target.
  • reset (Event | Effect | Store) — A unit that resets once to the initial state, allowing target to be triggered again.

Returns

  • target Event<T> — The event that will be triggered once after source is triggered, until once is reset by calling reset.

Example

import { createGate } from 'effector-react';

const PageGate = createGate();

sample({
source: once({
source: PageGate.open,
reset: fetchDataFx.fail,
}),
target: fetchDataFx,
});
- + \ No newline at end of file diff --git a/methods/or/index.html b/methods/or/index.html index 2e79d39d..a5f76da3 100644 --- a/methods/or/index.html +++ b/methods/or/index.html @@ -4,14 +4,14 @@ or | effector patronum - +

or

since

patronum 1.11.0

import { or } from 'patronum';
// or
import { or } from 'patronum/or';

Motivation

The method allows to check each passed store for truthy values. It can be compared with if (a || b || c || d).

Formulae

$result = or(...stores);
  • $result store contains true if at least one of passed store contains "truthy" value.

Arguments

The method receives any amount of arguments.

  • ...stores: Array<Store<T>> — Each argument must be store with a value of any kind.

Returns

  • $result: Store<boolean> — The store contains false if each passed store in stores contains "falsey" value

Example

const $isAuthorized = createStore(true);
const $immediateOrder = createStore(false);
const $mocksForDemo = createStore(false);

const $allowedToShow = or($isAuthorized, $immediateOrder, $mocksForDemo);
console.assert(true === $allowedToShow.getState());

Try it

Alternative

import { combine, createSTore } from 'effector';

const $isAuthorized = createStore(true);
const $immediateOrder = createStore(false);
const $mocksForDemo = createStore(false);

const $allowedToShow = combine(
$isAuthorized,
$immediateOrder,
$mocksForDemo,
(isAuthorized, immediateOrder, mocksForDemo) =>
isAuthorized || immediateOrder || mocksForDemo,
);
console.assert(true === $allowedToShow.getState());
- + \ No newline at end of file diff --git a/methods/pending/index.html b/methods/pending/index.html index 3f5e6269..ef552993 100644 --- a/methods/pending/index.html +++ b/methods/pending/index.html @@ -4,7 +4,7 @@ pending | effector patronum - + @@ -16,7 +16,7 @@ is usef when you want to show loading state of the whole application.

Formulae

$inProcess = pending({ domain, of: Strategy });
  • When an effect created in the domain in pending state, result will be true
  • The of parameter selects strategy

Arguments

  1. domain (Domain) - Effector domain with at least one effect
  2. of ("some" | "every") — Optional. Select strategy of combining pendings of differents effects. Default "some"

Returns

  • $inProcess (Store<boolean>) - Store with boolean state

Example

import { createDomain } from 'effector';
import { pending } from 'patronum/pending';

const app = createDomain();
const loadFirstFx = app.createEffect(() => Promise.resolve(null));
const loadSecondFx = app.createEffect(() => Promise.resolve(2));
const $processing = pending({ domain: app });

$processing.watch((processing) => console.info(`processing: ${processing}`));
// => processing: false

loadFirstFx();
loadSecondFx();
// => processing: true

Strategy

There available two options:

  • some — default strategy when of parameter is not provided. At least one effect must be in pending state.
  • every — each effect must be in pending state.

Example

import { createEffect } from 'effector';
import { pending } from 'patronum/pending';

const loadFirstFx = createEffect(() => Promise.resolve(null));
const loadSecondFx = createEffect(() => Promise.resolve(2));

const $pending = pending({ effects: [loadFirstFx, loadSecondFx], of: 'every' });

// When no effects is loading, $pending will be true

// If only one is loading, also will be false
loadFirstFx();

// But after running the second effect, $pending will be true
loadSecondFx();

$pending.watch(console.log); // true
- + \ No newline at end of file diff --git a/methods/previous/index.html b/methods/previous/index.html index 112ed4ac..287be8b3 100644 --- a/methods/previous/index.html +++ b/methods/previous/index.html @@ -4,13 +4,13 @@ previous | effector patronum - +

previous

since

patronum 2.1.0

import { previous } from 'patronum';
// or
import { previous } from 'patronum/previous';

Motivation

The method allows to get previous value of given store. Usually need for analytics

Formulae

$target = previous($source);
$target = previous($source, 'initial value');

Arguments

  1. $source ([Store]) - source store
  2. defaultValue (optional) - default value for $target store, if not passed, null will be used

Returns

  • $target ([Store]) - new store that contain previous value of $source after first update and null or default value (if passed) before that

Example

Push analytics with route transition:

import { createStore, createEvent, createEffect, sample } from 'effector';
import { previous } from 'patronum';

const openNewRoute = createEvent<string>();
const $currentRoute = createStore('main_page');
const $previousRoute = previous($currentRoute);

const sendRouteTransitionFx = createEffect(async ({ prevRoute, nextRoute }) => {
console.log(prevRoute, '->', newRoute)
await fetch(...)
});

sample({clock: openNewRoute, target: $currentRoute});

sample({
clock: openNewRoute,
source: {
prevRoute: $previousRoute,
nextRoute: $currentRoute,
},
target: sendRouteTransitionFx,
});

openNewRoute('messages');
// main_page -> messages
openNewRoute('chats');
// messages -> chats
- + \ No newline at end of file diff --git a/methods/readonly/index.html b/methods/readonly/index.html index 201e932d..457a461d 100644 --- a/methods/readonly/index.html +++ b/methods/readonly/index.html @@ -4,14 +4,14 @@ readonly | effector patronum - +

readonly

since

patronum 2.2.0

import { readonly } from 'patronum';
// or
import { readonly } from 'patronum/readonly';

Motivation

The method allows to convert writable store and callable event to their readonly versions. It can be helpful to create more strict public api.

Formulae

$result = readonly($store);
  • $result store contains mapped $store, which is readonly for consumers.
result = readonly(event);
  • result event contains mapped event, which is not callable by consumers.

Arguments

  • value: Store<T>|Event<T> — Any store or event, that required to be readonly

Returns

  • result: Store<T>|Event<T>

Note: if passed argument is already derived, then argument returns as-is.

Example

const $store = createStore({});
const $readonlyStore = readonly($store);

console.assert(false === is.targetable($readonlyStore));
const event = createEvent();
const readonlyEvent = readonly(event);

console.assert(false === is.targetable($readonlyStore));
- + \ No newline at end of file diff --git a/methods/reset/index.html b/methods/reset/index.html index e86ff2f6..29d9006d 100644 --- a/methods/reset/index.html +++ b/methods/reset/index.html @@ -4,13 +4,13 @@ reset | effector patronum - +

reset

since

patronum 1.7.0

import { reset } from 'patronum';
// or
import { reset } from 'patronum/reset';

reset({ clock, target })

Motivation

The method allow to reset many stores by a single line

Formulae

reset({ clock, target });
  • When clock is triggered, reset store/stores in target to the initial value.

Arguments

  1. clock: Unit<any> | Array<Unit<any>> — Any kind of units is accepted (Store, Event, Effect).
  2. target: Store<any> | Array<Store<any>> — Each of these stores will be reset to the initial values when clock is happened.

Example

import { createEvent, createStore } from 'effector';
import { reset } from 'patronum/reset';

const pageUnmounted = createEvent();
const userSessionFinished = createEvent();

const $post = createStore(null);
const $comments = createStore([]);
const $draftComment = createStore('');

reset({
clock: [pageUnmounted, userSessionFinished],
target: [$post, $comments, $draftComment],
});
import { createStore } from 'effector';
import { reset } from 'patronum/reset';

const $post = createStore(null);
const $comments = createStore([]);
const $draftComment = createStore('');

const resetEvent = reset({ target: [$post, $comments, $draftComment] });

Try it

Alternative

First variant is writing each reset by yourself:

$post.reset([pageUnmounted, userSessionFinished]);
$comments.reset([pageUnmounted, userSessionFinished]);
$draftComment.reset([pageUnmounted, userSessionFinished]);

There has another way — use domain:

import { createDomain, createStore } from 'effector';
const resetOnSomeCases = createDomain();

const $post = resetOnSomeCases.createStore(null);
const $comments = resetOnSomeCases.createStore([]);
const $draftComment = resetOnSomeCases.createStore('');

resetOnSomeCases.onCreateStore((store) => {
store.reset([pageUnmounted, userSessionFinished]);
});

reset({ target })

Motivation

The method allow to reset many stores by a single line with no clock passing

since

The clock argument became optional since patronum 1.15.0

Formulae

const resetEvent = reset({ target });
  • When resetEvent is triggered, reset store/stores in target to the initial value.

Arguments

  1. target: Store<any> | Array<Store<any>> — Each of these stores will be reset to the initial values when resetEvent is triggered.

Returns

  • resetEvent (Event<void>) — New event that reset store/stores in target.

Example

import { createEvent, createStore } from 'effector';
import { reset } from 'patronum/reset';

const $post = createStore(null);
const $comments = createStore([]);
const $draftComment = createStore('');

const resetEvent = reset({ target: [$post, $comments, $draftComment] });

Alternative

Write reset event by yourself:

import { createEvent, createStore } from 'effector';
import { reset } from 'patronum/reset';

const $post = createStore(null);
const $comments = createStore([]);
const $draftComment = createStore('');

const resetEvent = createEvent();

reset({
clock: resetEvent,
target: [$post, $comments, $draftComment],
});
- + \ No newline at end of file diff --git a/methods/reshape/index.html b/methods/reshape/index.html index 1fd6e022..5b14f192 100644 --- a/methods/reshape/index.html +++ b/methods/reshape/index.html @@ -4,14 +4,14 @@ reshape | effector patronum - +

reshape

import { reshape } from 'patronum';
// or
import { reshape } from 'patronum/reshape';

result = reshape({ source, shape })

Motivation

This method allows to create many stores from single store at once. It useful when you want to read many properties from object to different stores.

Formulae

result = reshape({ source, shape });
  • Call each function in shape object with data from source, and create store with the same name as key in shape
  • If function in shape returns undefined, the same store will contain null, because store cannot contain undefined.

Arguments

  1. source (Store) — Source store, data from this unit passed to each function in shape
  2. shape ({ [key: string]: (payload: T) => any }) — Object of functions. Function receives payload of source as single argument, should return any value.

Returns

  • result ({ [key: string]: Store<any> }) — Object of stores, that created with the same structure as shape, with data from passed functions

Examples

Creates much stores from single string

import { createStore } from 'effector';
import { reshape } from 'patronum/reshape';

const $original = createStore<string>('Example');

const result = reshape({
source: $original,
shape: {
length: (string) => string.length,
lowercase: (string) => string.toLowerCase(),
uppercase: (string) => string.toUpperCase(),
},
});

// result.length: Store<number>;
// result.lowercase: Store<string>;
// result.uppercase: Store<string>;

result.length.watch((length) => console.log('String length:', length));
result.lowercase.watch((lowercase) => console.log('lowercase:', lowercase));

Destructure object

import { createStore } from 'effector';
import { reshape } from 'patronum/reshape';

const $user = createStore({
name: 'Sergey',
lastname: 'Sova',
age: 26,
});

const { name, lastname, age } = reshape({
source: $user,
shape: {
name: (user) => user.name,
lastname: (user) => user.lastname,
age: ({ age }) => age,
},
});

name.watch((name) => console.info('name updated', name));
lastname.watch((lastname) => console.info('lastname updated', lastname));
age.watch((age) => console.info('age updated', age));

// => name updated Sergey
// => lastname updated Sova
// => age updated 26
- + \ No newline at end of file diff --git a/methods/snapshot/index.html b/methods/snapshot/index.html index 9299148f..7dfca43d 100644 --- a/methods/snapshot/index.html +++ b/methods/snapshot/index.html @@ -4,14 +4,14 @@ snapshot | effector patronum - +

snapshot

since

patronum 1.7.0

import { snapshot } from 'patronum';
// or
import { snapshot } from 'patronum/snapshot';

result = snapshot({ source, clock, fn })

Motivation

This method allows to copy any store on optional trigger unit. It useful when you want to save previous state of store before some actions.

Formulae

result = snapshot({ source, clock, fn });
  • Call fn with data from source while clock triggered, and create store with the value
  • If fn returns undefined, the update will be skipped.

Arguments

  1. source (Store) — Source store, data from this unit passed to fn
  2. clock (Event, Effect, Store) — Trigger unit
  3. fn ((value: T) => U) — Transformation function

Returns

  • result (Store) — Copied store

Examples

Exact copy of store

import { createStore } from 'effector';
import { snapshot } from 'patronum/snapshot';

const $original = createStore<string>('Example');

const $copy = snapshot({ source: $original });

Exact copy on trigger

import { restore, createEvent } from 'effector';
import { snapshot } from 'patronum/snapshot';

const changeText = createEvent<string>();
const createSnapshot = createEvent();

const $original = restore(changeText, 'Example');

const $snapshot = snapshot({
source: $original,
clock: createSnapshot,
});

changeText('New text');

// $original -> Store with "New text"
// $snapshot -> Store with "Example"

createSnapshot();

// $original -> Store with "New text"
// $snapshot -> Store with "New text"

Copy on trigger with transformation

import { restore, createEvent } from 'effector';
import { snapshot } from 'patronum/snapshot';

const changeText = createEvent<string>();
const createSnapshot = createEvent();

const $original = restore(changeText, 'Example');

const $lengthSnapshot = snapshot({
source: $original,
clock: createSnapshot,
fn: (text) => text.length,
});

// $original -> Store with "Example"
// $lengthSnapshot -> Store with 7 (length of "Example")

changeText('New long text');

// $original -> Store with "New long text"
// $lengthSnapshot -> Store with 7 (length of "Example")

createSnapshot();

// $original -> Store with "New long text"
// $lengthSnapshot -> Store with 13 (length of "New long text")
- + \ No newline at end of file diff --git a/methods/some/index.html b/methods/some/index.html index 6dd4421a..3be6708f 100644 --- a/methods/some/index.html +++ b/methods/some/index.html @@ -4,7 +4,7 @@ some | effector patronum - + @@ -13,7 +13,7 @@ It is useful to check that user filled at least a single field.

Formulae

$result = some({ predicate: (value) => true, stores });
  • read it as: has some predicate at at least one store
  • $result will be true if each at least predicate on each store value from values returns true, otherwise it will be false

Arguments

  1. predicate ((value: T) => boolean) — Function to check store value
  2. stores (Array<Store<T>>) — List of stores

Return

  • $result (Store<boolean>)true if at least one store corresponds to predicate

Example

const $width = createStore(440);
const $height = createStore(820);

const $tooBig = some({
predicate: (size) => size > 800,
stores: [$width, $height],
});

console.assert(true === $tooBig.getState());

some({ predicate: value, stores })

Motivation

This overload compares each store to specific value in predicate. It is useful when you write combine with || very often, for example to create an invalid form flag.

Formulae

$result = some({ predicate: value, stores });
  • $result will be true if at least one value in stores equals value, otherwise it will be false

Arguments

  1. predicate (T) — Data to compare stores values with
  2. stores (Array<Store<T>>) — List of stores to compare with value
  3. type of predicate and stores should should be the same

Return

  • $result (Store<boolean>)true if at least one store contains value

Example

const $isPasswordCorrect = createStore(true);
const $isEmailCorrect = createStore(true);

const $isFormFailed = some({
predicate: false,
stores: [$isPasswordCorrect, $isEmailCorrect],
});

console.assert(false === $isFormFailed.getState());

some({ predicate: Store, stores })

since

patronum 1.8.0

Motivation

This overload compares each store to specific value in store predicate. It is useful when you write combine with || very often, for example to create an invalid form flag.

Formulae

$result = some({ predicate: $value, stores });
  • $result will be true if at least one value in stores equals value in $value, otherwise it will be false

Arguments

  1. predicate (Store<T>) — Store contains value to compare values from stores with
  2. stores (Array<Store<T>>) — List of stores to compare with value
  3. type of value and stores should be the same

Return

  • $result (Store<boolean>)true if at least one store contains value

Example

const $allowToCompare = createStore(true);

const $isPasswordCorrect = createStore(true);
const $isEmailCorrect = createStore(true);

const $isFormFailed = some({
predicate: $allowToCompare,
stores: [$isPasswordCorrect, $isEmailCorrect],
});

console.assert(false === $isFormFailed.getState());

Shorthands

$result = some(stores, value);
$result = some(stores, (value) => false);
$result = some(stores, $predicate);

Shorthand have the same rules as the main overrides, just it uses positional arguments instead of object-form.

Arguments

  1. stores (Array<Store<T>>) — List of stores to compare with predicate in the second argument
  2. predicate (Store<T> | (value: T) => boolean | T) — Predicate to compare with
- + \ No newline at end of file diff --git a/methods/split-map/index.html b/methods/split-map/index.html index 60fb1002..09748fd5 100644 --- a/methods/split-map/index.html +++ b/methods/split-map/index.html @@ -4,14 +4,14 @@ splitMap | effector patronum - +

splitMap

import { splitMap } from 'patronum';
// or
import { splitMap } from 'patronum/split-map';

shape = splitMap({ source, cases })

Motivation

The method is a combination of split and map. It is useful when you want add some kind of pattern matching.

Formulae

shape = splitMap({ source, cases });
  • On each source trigger, call each function in cases object one after another until function returns non undefined, and call event in shape with the same name as function in cases object.
  • If no function returned value event __ in shape should be triggered

Arguments

  1. source (Event | Store | Effect) — Source unit, data from this unit passed to each function in cases object and __ event in shape as is
  2. cases ({ [key: string]: (payload: T) => any | void }) — Object of functions. Function receives one argument is a payload from source, should return any value or undefined

Returns

  • shape ({ [key: string]: Event<any>; __: Event<T> }) — Object of events, with the same structure as cases, but with the default event __, that triggered when each other function returns undefined

Examples

Extract passed fields from optional object

import { createEvent } from 'effector';
import { splitMap } from 'patronum/split-map';

const event = createEvent<object>();

const shape = splitMap({
source: event,
cases: {
getType: (input) => input.type,
getDemo: (input) => input.demo,
},
});

shape.getType.watch((type) => console.log('TYPE', type));
shape.getDemo.watch((demo) => console.log('DEMO', demo));
shape.__.watch((other) => console.log('OTHER', other));

event({ type: 'demo' });
// => TYPE demo

event({ demo: 5 });
// => DEMO 5

event({});
// => OTHER {}

Split WebSocket events to effector events

import { createEvent } from 'effector';
import { splitMap } from 'patronum/split-map';

type WSEvent =
| { type: 'init'; key: string }
| { type: 'increment'; count: number; name: string }
| { type: 'reset'; name: string };

export const websocketEventReceived = createEvent<WSEvent>();

const { init, increment, reset, __ } = splitMap({
source: websocketEventReceived,
cases: {
init: (event) => {
if (event.type === 'init') return event.key;
},
increment: ({ type, ...payload }) => {
if (type === 'increment') return payload;
},
reset: ({ type, name }) => {
if (type === 'reset') return name;
},
},
});

__.watch((payload) => {
console.warn('Unknown type:', payload.type);
});

increment.watch((payload) => {
console.info('should be incremented', payload.count, payload.name);
});

websocketEventReceived({ type: 'increment', name: 'demo', count: 5 });
// => should be incremented 5 'demo'

websocketEventReceived({ type: 'bang', random: 'unknown' });
// => Unknown type: 'bang'
- + \ No newline at end of file diff --git a/methods/spread/index.html b/methods/spread/index.html index 967043ef..124939f7 100644 --- a/methods/spread/index.html +++ b/methods/spread/index.html @@ -4,16 +4,16 @@ spread | effector patronum - +

spread

import { spread } from 'patronum';
// or
import { spread } from 'patronum/spread';

source = spread(targets)

since

patronum 2.1.0 Use spread({ targets }) with patronum < 2.1.0

Motivation

This method allows to trigger many target at once, if they match the source structure. -It is useful when you need to destructure object and save values to different stores.

Formulae

source = spread({ field: target, ... })
  • When source is triggered with object, extract field from data, and trigger target
  • targets can have multiple properties
  • If the source was triggered with non-object, nothing would be happening
  • If source is triggered with object but without propertpy field, target for this field will not be triggered

Arguments

  1. targets (Record<string, Event<T> | Store<T> | Effect<T>>) — Flat object which key is key in source payload, and value is unit to store value to.

Returns

  • source (Event<T>) — Source event, data passed to it should be an object with fields from targets

Example

Conditionally save value to stores

import { createStore, createEvent, sample } from 'effector';
import { spread } from 'patronum';

const $first = createStore('');
const $last = createStore('');

const formReceived = createEvent();

sample({
source: formReceived,
filter: (form) => form.first.length > 0 && form.last.length > 0,
target: spread({
first: $first,
last: $last,
}),
});

$first.watch((first) => console.log('First name', first));
$last.watch((last) => console.log('Last name', last));

formReceived({ first: '', last: '' });
// Nothing, because filter returned true

formReceived({ first: 'Hello', last: 'World' });
// => First name Hello
// => Last name World

Nested spreading

const $targetA = createStore('');
const $targetB = createStore(0);
const $targetC = createStore(false);

const trigger = spread({
first: $targetA,
second: spread({
foo: $targetB,
bar: $targetC,
}),
});

$targetA.watch((payload) => console.log('targetA', payload));
$targetB.watch((payload) => console.log('targetB', payload));
$targetC.watch((payload) => console.log('targetC', payload));

trigger({
first: 'Hello',
second: {
foo: 200,
bar: true,
},
});
// => targetA Hello
// => targetB 200
// => targetC true

spread({ source, targets })

Motivation

This method allows to trigger many target at once, if they match the source structure. +It is useful when you need to destructure object and save values to different stores.

Formulae

source = spread({ field: target, ... })
  • When source is triggered with object, extract field from data, and trigger target
  • targets can have multiple properties
  • If the source was triggered with non-object, nothing would be happening
  • If source is triggered with object but without propertpy field, target for this field will not be triggered

Arguments

  1. targets (Record<string, Event<T> | Store<T> | Effect<T>>) — Flat object which key is key in source payload, and value is unit to store value to.

Returns

  • source (Event<T>) — Source event, data passed to it should be an object with fields from targets

Example

Conditionally save value to stores

import { createStore, createEvent, sample } from 'effector';
import { spread } from 'patronum';

const $first = createStore('');
const $last = createStore('');

const formReceived = createEvent();

sample({
source: formReceived,
filter: (form) => form.first.length > 0 && form.last.length > 0,
target: spread({
first: $first,
last: $last,
}),
});

$first.watch((first) => console.log('First name', first));
$last.watch((last) => console.log('Last name', last));

formReceived({ first: '', last: '' });
// Nothing, because filter returned false

formReceived({ first: 'Hello', last: 'World' });
// => First name Hello
// => Last name World

Nested spreading

const $targetA = createStore('');
const $targetB = createStore(0);
const $targetC = createStore(false);

const trigger = spread({
first: $targetA,
second: spread({
foo: $targetB,
bar: $targetC,
}),
});

$targetA.watch((payload) => console.log('targetA', payload));
$targetB.watch((payload) => console.log('targetB', payload));
$targetC.watch((payload) => console.log('targetC', payload));

trigger({
first: 'Hello',
second: {
foo: 200,
bar: true,
},
});
// => targetA Hello
// => targetB 200
// => targetC true

spread({ source, targets })

Motivation

This method allows to trigger many target at once, if they match the source structure. It is useful when you need to destructure object and save values to different stores.

Formulae

spread({ source, targets: { field: target, ... } })
  • When source is triggered with object, extract field from data, and trigger target
  • targets can have multiple properties
  • If the source was triggered with non-object, nothing would be happening
  • If source is triggered with object but without propertpy field, target for this field will not be triggered

Arguments

  1. source (Event<T> | Store<T> | Effect<T>) — Source unit, data passed to it should be an object with fields from targets
  2. targets (Record<string, Event<T> | Store<T> | Effect<T>>) — Flat object which key is key in source payload, and value is unit to store value to.

Example

Save fields of payload to different stores

import { createStore, createEvent } from 'effector';
import { spread } from 'patronum';

const $first = createStore('');
const $last = createStore('');

const formReceived = createEvent();

spread({
source: formReceived,
targets: {
first: $first,
last: $last,
},
});

$first.watch((first) => console.log('First name', first));
$last.watch((last) => console.log('Last name', last));

formReceived({ first: 'Sergey', last: 'Sova' });
// => First name Sergey
// => Last name Sova

formReceived({ first: 'Patronum' });
// => First name Patronum

Call events on store changes

import { createStore, createEvent } from 'effector';
import { spread } from 'patronum/spread';

const save = createEvent();
const $form = createStore(null).on(save, (_, form) => form);

const firstNameChanged = createEvent();
const lastNameChanged = createEvent();

spread({
source: $form,
targets: {
first: firstNameChanged,
last: lastNameChanged,
},
});

firstNameChanged.watch((first) => console.log('First name', first));
lastNameChanged.watch((last) => console.log('Last name', last));

save({ first: 'Sergey', last: 'Sova' });
// => First name Sergey
// => Last name Sova

save(null);
// Nothing, because store is null

source = spread({ targets })

Motivation

This overload recieves targets as an object. May be useful for additional clarity, but it's longer to write

Formulae

source = spread({ targets: { field: target, ... } })
  • When source is triggered with object, extract field from data, and trigger target
  • targets can have multiple properties
  • If the source was triggered with non-object, nothing would be happening
  • If source is triggered with object but without propertpy field, target for this field will not be triggered

Arguments

  1. targets (Record<string, Event<T> | Store<T> | Effect<T>>) — Flat object which key is key in source payload, and value is unit to store value to.

Returns

  • source (Event<T>) — Source event, data passed to it should be an object with fields from targets
- + \ No newline at end of file diff --git a/methods/status/index.html b/methods/status/index.html index 75d6ae9a..3cbce02a 100644 --- a/methods/status/index.html +++ b/methods/status/index.html @@ -4,7 +4,7 @@ status | effector patronum - + @@ -12,7 +12,7 @@

status

import { status } from 'patronum';
// or
import { status } from 'patronum/status';

Motivation

This method returns current status of effect as store with string enumeration. It is useful to show correct state of process in UI.

Formulae

$status = status(effect);
$status = status({ effect, defaultValue });
  • When status is run, set defaultValue value to $status
  • When effect is called, set pending status.
  • When effect is succeeded, set done status.
  • When effect is failed, set fail status.
note

Shorthand status(effect) is available since patronum 2.1.0 Use status({ effect }) with patronum < 2.1.0

Arguments

  1. effect (Effect<P, R>) — any effect, that you need to watch status
  2. defaultValue ('initial' | 'pending' | 'done' | 'fail') optional — when $status initializes, set initial value for it. By default value is "initial"

Returns

  • $status (Store<'initial' | 'pending' | 'done' | 'fail'>) — Store that saves current state of the effects

Note: use can manually reset status, just use $status.reset(event)

Example

Successful effect call changing status to "done"

import { createEvent, createEffect } from 'effector';
import { status } from 'patronum/status';

const effect = createEffect(() => Promise.resolve(null));
const $status = status({ effect });

$status.watch((value) => console.log(`status: ${value}`));
// => status: "initial"

effect();
// => status: "pending"
// => status: "done"

Initial status

import { createEvent, createEffect } from 'effector';
import { status } from 'patronum/status';

const effect = createEffect(() => Promise.resolve(null));
const $status = status({ effect, defaultValue: 'pending' });

$status.watch((value) => console.log(`status: ${value}`));
// => status: "pending"

effect();
// Nothing, because $status is already pending
// => status: "done"

Clear (reset) status

import { createEvent, createEffect } from 'effector';
import { status } from 'patronum/status';

const reset = createEvent();
const effect = createEffect(() => new Promise((resolve) => setTimeout(resolve, 100)));

const $status = status({ effect });
$status.reset(reset);

$status.watch((value) => console.log(`status: ${value}`));
// => status: "initial"

effect();
// => status: "pending"
// => status: "done"

reset();
// => status: "initial"
- + \ No newline at end of file diff --git a/methods/throttle/index.html b/methods/throttle/index.html index e9668424..0f66767b 100644 --- a/methods/throttle/index.html +++ b/methods/throttle/index.html @@ -4,7 +4,7 @@ throttle | effector patronum - + @@ -13,7 +13,7 @@ It is useful in live search in UI.

since

patronum 2.1.0 Use throttle({ source, timeout }) with patronum < 2.1.0

Formulae

target = throttle(source, timeout);
  • Triggers target at most once per timeout after triggering source

Arguments

  1. source (Event | Store | Effect) — Source unit, data from this unit used by the target
  2. timeout (number | Store<number>) — time to wait before trigger target after last trigger or source trigger

Returns

  • target (Event) — new event, that triggered each time after triggering source with argument from source

Usage

Create event that should be throttled:

import { createEvent } from 'effector';

const someHappened = createEvent<number>();

Create throttled event from it:

import { throttle } from 'patronum';

const THROTTLE_TIMEOUT_IN_MS = 200;

const throttled = throttle(someHappened, THROTTLE_TIMEOUT_IN_MS);

When you call someHappened it will make throttled call the throttled event:

throttled.watch((payload) => {
console.info('someHappened now', payload);
});

someHappened(1);
someHappened(2);
someHappened(3);
someHappened(4);

// after 200 ms after first call
// => someHappened now 4

Also you can use Effect and Store as trigger. throttle always returns Event:

const event = createEvent<number>();
const throttledEvent: Event<number> = throttle(event, 100);

const fx = createEffect<number, void>();
const throttledEffect: Event<number> = throttle(fx, 100);

const $store = createStore<number>(0);
const throttledStore: Event<number> = throttle($store, 100);

throttle({ source, timeout, target })

Motivation

This overload allows to receive target instead of returning it. This is useful when you already have a unit that should be triggered.

Formulae

throttle({ source, timeout, target });
  • Triggers target at most once per timeout after triggering source

Arguments

  1. source (Event | Store | Effect) — Source unit, data from this unit used by the target
  2. timeout (number | Store<number>) — time to wait before trigger target after last trigger or source trigger
  3. target (Event | Store | Effect) — Target unit, that triggered each time after triggering source with argument from source

Returns

Usage

const change = createEvent();
const $source = createStore(0).on(change, (state) => state + 1);

const $dumped = createStore(0);
$dumped.watch((payload) => {
localStorage.setItem('dump', JSON.stringify(payload));
});

throttle({ source: $source, timeout: 40, target: $dumped });

change();
change();
change();

// after 40ms after first call, 3 will be saved to localStorage

Example with timeout as store

The new timeout will be used after the previous is over (if there was a delayed target trigger when the timeout store changed).

import { createEvent } from 'effector';
import { throttle } from 'patronum';

const someHappened = createEvent<number>();
const changeTimeout = createEvent<number>();
const $timeout = createStore(200).on(changeTimeout, (_, value) => value);

const throttled = throttle({
source: someHappened,
timeout: $timeout,
});
throttled.watch((payload) => {
console.info('someHappened now', payload);
});

someHappened(1);
changeTimeout(300); // will be used for next timeout
someHappened(2);

setTimout(() => {
// console.log: someHappened now 2
someHappened(3);
someHappened(4);
}, 200);

setTimeout(() => {
// console.log: someHappened now 4
}, 500);

target = throttle({ source, timeout })

Motivation

This overload recieves source and timeout as an object. May be useful for additional clarity, but it's longer to write

Formulae

target = throttle({ source, timeout });
  • Triggers target at most once per timeout after triggering source

Arguments

  1. source (Event | Store | Effect) — Source unit, data from this unit used by the target
  2. timeout (number | Store<number>) — time to wait before trigger target after last trigger or source trigger

Returns

  • target (Event) — new event, that triggered each time after triggering source with argument from source

Usage

Create event that should be throttled:

import { createEvent } from 'effector';

const someHappened = createEvent<number>();

Create throttled event from it:

import { throttle } from 'patronum';

const THROTTLE_TIMEOUT_IN_MS = 200;

const throttled = throttle({
source: someHappened,
timeout: THROTTLE_TIMEOUT_IN_MS,
});

When you call someHappened it will make throttled call the throttled event:

throttled.watch((payload) => {
console.info('someHappened now', payload);
});

someHappened(1);
someHappened(2);
someHappened(3);
someHappened(4);

// after 200 ms after first call
// => someHappened now 4

Also you can use Effect and Store as trigger. throttle always returns Event:

const event = createEvent<number>();
const throttledEvent: Event<number> = throttle({ source: event, timeout: 100 });

const fx = createEffect<number, void>();
const throttledEffect: Event<number> = throttle({ source: fx, timeout: 100 });

const $store = createStore<number>(0);
const throttledStore: Event<number> = throttle({
source: $store,
timeout: 100,
});
- + \ No newline at end of file diff --git a/methods/time/index.html b/methods/time/index.html index 8e4c2f0b..521dad94 100644 --- a/methods/time/index.html +++ b/methods/time/index.html @@ -4,7 +4,7 @@ time | effector patronum - + @@ -15,7 +15,7 @@ If getNow is overrided, contains value this function returns. By default, it is number. Initialized with initial, by default, it is Date.now()

Example

import { time } from 'patronum';

const tick = createEvent();
const $time = time({ clock: tick });

$time.watch((time) => console.log('time', time));
// => time 1660293358847

tick();
// => time 1660293358848
await new Promise((res) => setTimeout(res, 100));
tick();
// => 1660293358952

Try it

- + \ No newline at end of file diff --git a/search/index.html b/search/index.html index 4bed12b5..5c885e4e 100644 --- a/search/index.html +++ b/search/index.html @@ -4,13 +4,13 @@ Search the documentation | effector patronum - + - + \ No newline at end of file