Skip to content

Instantly share code, notes, and snippets.

@AlexandrHoroshih
Last active January 19, 2022 18:57
Show Gist options
  • Save AlexandrHoroshih/629936aa9165bc50c94a0668934602ca to your computer and use it in GitHub Desktop.
Save AlexandrHoroshih/629936aa9165bc50c94a0668934602ca to your computer and use it in GitHub Desktop.

As for now, every unit in effector must have an unique stable identifier and factories of units must be marked at effector/babel-plugin settings in a special way by adding path to factory definiton to factories list, so every group of units created from factory call have ther own sids and other metadata.

As it turns out, this way can be really confusing for many developers and, while it fairly easy to use with external libs like patronum or @effector/reflect, this maybe hard to notice if something went wrong when dealing with project-specific custom factories

Proposal

I think, if there is standardized api to define and use effector factories in the code

It can work as a pair of methods:

factory

First method to mark definiton of the factory, so babel-plugin can add some metadata to it, which can be useful for dev tools

import { factory, ... } from "effector"

export const createForm = factory((config) => ...)

// `factory` returns non-callable object

withFactory

Second method used for every call of factories:

import { withFactory } from "effector"
import { createForm } from "shared/lib/effector--my-forms"

const loginForm = withFactory(createForm, { fields: ... })

// making `factory` return non-callable object will ensure,
// that all effector factories created in the correct and detectable way

This way calls of custom factories are all marked by withFactory and effector/babel-plugin can add sid to every withFactory call, just like for original methods like createEffect - without additional configuration

Output of the plugin can be something like this:

const loginForm = withFactory({
 fn: createForm.fn,
 params: { fields: ... },
 sid: "f78dsgkn9",
 // some other metadata, if needed
})

Use case

Good parts:

  • The explicit way of calling the factory allows you not to rely on a specific path - there is no need to keep track if another developer has moved the factory to a different location than the one specified in the babel-plugin settings
  • I think special api is less confusing - it is basically "just write code like in the docs" versus "add import path of every custom factory to babel-plugin settings, make sure it is added in correct way, make sure there is no other ways to import this factory, etc"
  • Current way of defining factories is still useful for external factories and libs like @effector/reflect (it is easy to just add the name of the lib to factories)

Problems:

  • a lot of withFactory calls will be quite noisy in the source code. Maybe shorter name like f(createForm, config) could help?
  • instead of path-related problems comes possible issue with createStuff = (params) => withFactory(factory, params) - nested factory call without wrapper at the top
  • since current path-based way cannot be removed, we will end up with to ways to do the same thing - both with different pros and cons
@Kelin2025
Copy link

+++ on that. Although it will add more API to effector, we'll actually have a full understanding on whether something is a factory or not. It's useful not only for sids generation but will also probably help with forest lists - we could call factories inside list items and get some extra optimizations

@Kelin2025
Copy link

By the way, not so long ago, I made effector-factorio which uses a bit similar idea - you create factories with modelFactory(config => ...) method and then create instance provider hocs with modelView(model, View). Although without withFactory method

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment