effector / reflect

Easily render React components with everything you love from ☄️ effector.

Home Page:https://reflect.effector.dev/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Dynamic `bind` API

Kelin2025 opened this issue · comments

This RFC solves impossibility to combine @effector/reflect and effector-factorio, as well as adding protocol to integrate other dynamic bindings in a future

How does it look?

It looks like this:

import { reflect, take } from '@effector/reflect'

const SomeInput = reflect({
  view: Input,
  bind: {
    value: take(something, key)
  }
})

take returns the following structure:

{
  /** Telling `reflect` how to extract the dynamic source of units */
  useSource: props => something.useSource(props),
  /** `key` that takes unit from  source */
  key: key
}

In effector-factorio we will add this alias:

factory.useSource = factory.useModel // Takes model instance from React.Context

And then we will be able to combine them:

const factory = modelFactory(() => {
  return {
    $value: createStore("")
  }
})

const SomeInput = reflect({
  view: Input,
  bind: {
    value: take(factory, "$value")
  }
})

Also

  • We should also support (source, props) => key variant for key in order to support dynamic factories (they're not welcome but technically possible)
  • We could also add fromProps(key) === take({ useSource: props => props, key) shorthand that allows to take store from just props

Why this API? Why not bind: props => ({ ... })?

  • No breaking changes
  • We still statically get all non-dynamic bindings
  • This API is not limited/specific for effector-factorio only, it doesn't even have its mention
  • Adds only 10 lines of code in @effector/reflect and 1 alias in effector-factorio

There is a lot of ways in React to get dynamic data using hooks.

// Simple hook
const data = useHook();

// Hook with params
const data = useHook(params);

// Hook with callback
const data = useHook(() => new Class());

// Hook returning array
const [data] = useHook();

// Hook returning object
const {data} = useHook();

// Hook that depends on other hook data
const dataOne = useHookOne();
const dataTwo = useHookTwo(dataOne); 

Also with your implementation if you use multiple keys from the factory

  bind: {
    value: take(factory, "$value"),
    valueChange: take(factory, "valueChange")
  }

The factory useModel() will be called N times, where N is the amount of wanted keys.


It might be much more flexible to add a hook property that will gather all the data and then add it to a bind values.

const SomeInput = reflect({
  view: Input,
  bind: {
    value: $value
  },
  useDynamicBind: (componentProps) => {
    const hookWithPropsData = useHook(componentProps.transactionId);
    const hookThatDependsData = useDependentHook(hookWithPropsData);
    const {$store, storeChange} = factory.useModel();

    return { $store, storeChange, hookThatDependsData }
  }
})
commented

What about API like this?

const SomeInput = reflect({
  view: Input,
  bind: {
    value: factory.fromModel((model) => model.$value),
  },
})
commented

But actually I think we can implement take(factory, 'key')

With factory.fromModel factorio will know about reflect. And we'll also have to make the same for keyval and any other library 🤔

commented

I think factory.fromModel can return some internal object implement by protocol.

{
  useSource: props => something.useSource(props),

  fn: () => Unit
}