Effector Patronum

repository·main·Indexed 43 days ago

https://github.com/effector/patronum

A library of operators for Effector that provides modularity and convenience for managing data flows and business logic. It includes utilities such as the status operator for tracking effect lifecycles, logical predicates like and, and flow control operators including debounce, condition, and combineEvents.

Tokens
42.4K
Snippets
152
Records
166
Agent score
54%

What's inside Patronum

  1. How to nest `condition` calls

    main

    Because condition returns a Unit<T> (the same type as the source), you can nest condition calls to create complex conditional logic trees. This allows you to perform sequential checks on a single source payload.

    const $value = createStore("hello@world");
    const updateEmail = createEvent<string>();
    
    condition({
      source: $value,
      if: (length) => length > 0,
      then: condition({
        if: (string) => string.includes("@"),
        then: updateEmail,
      }),
    });
  2. Use splitMap for pattern matching and data extraction

    main

    The splitMap function is a combination of split and map. It allows you to perform pattern matching on a source unit (Event, Store, or Effect) and transform the payload into specific events based on the result of provided functions.

    How it works

    1. When the source triggers, splitMap executes each function in the cases object sequentially.
    2. If a function returns a non-undefined value, the corresponding event in the returned shape is triggered with that value.
    3. If no function in cases returns a value (i.e., all return undefined), the default event __ is triggered with the original payload from the source.
    import { splitMap } from 'patronum';
    // or
    import { splitMap } from 'patronum/split-map';
  3. Understand pending combination strategies: 'some' vs 'every'

    main

    When monitoring multiple effects, you can choose how their pending states are aggregated using the of parameter:

    • some (Default): The resulting store is true if any effect is currently pending. Use this for general loading indicators where any background activity should trigger a loading state.
    • every: The resulting store is true only if all specified effects are currently pending. Use this when you want to wait for a complete set of parallel operations to finish before hiding a loading state.
  4. Compare `and` with `combine` for logical operations

    main

    While and(...stores) is a concise way to check if all stores are truthy, you can achieve the same result using combine if you need more complex logic or access to the specific values of the stores.

    Use and for simple truthy checks to reduce boilerplate. Use combine when you need to perform operations on the values themselves or implement non-trivial logic.

    // Using combine as an alternative to and/or/not composition
    const $showRegisterLink = combine(
      $isAuthorized,
      $isRegisterInProcess,
      $isEmailRecovering,
      (isAuthorized, isRegisterInProcess, isEmailRecovering) => {
        return !isAuthorized && (isRegisterInProcess || isEmailRecovering);
      },
    );
  5. Use interval with the @@trigger protocol

    main

    The interval function supports the @@trigger protocol. If you do not provide start and stop options, the returned object can be used in any context that accepts a trigger.

    This is particularly useful for automatically refreshing data in libraries like Farfetched by passing an interval as a trigger.

    import { keepFresh } from '@farfetched/core';
    import { interval } from 'patronum';
    
    keepFresh(someQuery, {
      // 👇 Query will be refreshed each 1000 ms
      triggers: [interval({ timeout: 1000 })],
    });
  6. Enable computation traces in `debug`

    main

    By setting { trace: true } in the configuration object, debug will log computation traces. This shows the flow of updates through the system. It is highly recommended to use this feature alongside the effector/babel-plugin for accurate source mapping and node identification.

    When trace: true is enabled, the logs will include the sequence of operations that led to the current state.

    const inputChanged = createEvent();
    const $form = createStore(0).on(inputChanged, (s) => s + 1);
    
    // Enables trace logging for the provided units
    debug({ trace: true }, $form, inputChanged);
    
    inputChanged();
  7. How the spread utility works

    main

    The spread utility allows you to trigger multiple Effector units (Stores, Events, or Effects) simultaneously by destructuring an object payload. When the source unit is triggered with an object, spread extracts specific fields from that object and sends them to the corresponding target units.

    Key Behaviors:

    • Object Requirement: If the source is triggered with a non-object payload, nothing happens.
    • Missing Fields: If the payload is an object but lacks a specific field defined in targets, the corresponding target unit will not be triggered.
    • Nested Spreading: You can nest spread calls to handle deeply nested object structures.
    • Multiple Targets: You can pass an array of units for a single field to trigger multiple units at once with the same value.
    // Nested spreading example
    const trigger = spread({
      first: $targetA,
      second: spread({
        foo: $targetB,
        bar: $targetC,
      }),
    });
    
    trigger({
      first: 'Hello',
      second: {
        foo: 200,
        bar: true,
      },
    });
  8. Understand the Starlight project structure

    main

    A Starlight project follows a specific directory structure for content and assets:

    • src/content/docs/: Place your .md or .mdx files here. Each file is automatically exposed as a route based on its filename.
    • src/assets/: Store images here to be embedded in Markdown using relative links.
    • public/: Store static assets like favicons.
    • astro.config.mjs: The main configuration file for Astro.
    • src/content/config.ts: Configuration for content collections.
    .
    ├── public/
    ├── src/
    │   ├── assets/
    │   ├── content/
    │   │   ├── docs/
    │   │   └── config.ts
    │   └── env.d.ts
    ├── astro.config.mjs
    ├── package.json
    └── tsconfig.json
  9. Use a Store to dynamically control throttle timeout

    main

    You can pass a Store<number> as the timeout argument. The throttle mechanism will use the current value of the store. If the store value changes, the new timeout will be applied to the next interval (after the current pending timeout has finished).

    import { createEvent, createStore } 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); // This 300ms value will be used for the next timeout cycle
    someHappened(2);
  10. Import patronum methods

    main

    You can import methods from patronum using either the root module or individual sub-modules.

    Root module import: Use this for convenience to access multiple methods from a single import.

    Sub-module import: Use this for better tree-shaking or specific imports. Note the naming convention: while method exports are camelCase, the sub-module paths use param-case (e.g., patronum/split-map).

    Important Note: The patronum/debug module is only available via sub-module import and cannot be accessed from the root module.

    // Root module import
    import { throttle, splitMap } from 'patronum';
    
    // Sub-module import (param-case naming)
    import { throttle } from 'patronum/throttle';
    import { splitMap } from 'patronum/split-map';
    
    // Debugging (sub-module import only)
    import { debug } from 'patronum/debug';