effector icon indicating copy to clipboard operation
effector copied to clipboard

Simplify handling of multiple derived effects

Open olegKusov opened this issue 3 years ago • 3 comments

Proposal

Know for me it's too hard to handle multiple derived effects. Patronum has pending() but it doesnt help a lot:

const $processing = pending({ effects: [loadFirst, loadSecond] });
export const $message = createStore('');

export const helloFx = createEffect(async () => {
  return new Promise(resolve => setTimeout(() => resolve('hello'), 3000))
})

export const byeFx = createEffect(async () => {
  return new Promise(resolve => setTimeout(() => resolve('bye'), 500))
})

const changeEvent = createEvent() // changeEvent triggers someFx3

const $processing = patronum.pending({
  effects: [helloFx, byeFx],
})

sample({
  clock: changeEvent,
  target: [helloFx, byeFx],
})

//now what if i need to construct store from fetched data?
// i can do this? No, becase it runs each time separately
sample({
  clock: [helloFx.doneData, byeFx.doneData],
  source: $processing,
  fn: (processing, data) => {
  },
  target: $message,
})

// i can construct it like that, but it's too hard to handle.

$message.on(helloFx.doneData, (message, data) => {
  if(message != '') {
  	return `${data}${message}`
  } else {
  	return `${data}_`;
  }
});

$message.on(byeFx.doneData, (message, data) => {
  if(message != '') {
  	return `${message}${data}`
  } else {
  	return `_${data}`;
  }
})

// we can do this
// but it's a lot of work too, because we need to create two separate stores for data.
const $helloRes = createStore('');
const $byeRes = createStore('');
$helloRes.on(helloFx.doneData, (_, data) => data);
$byeRes.on(byeFx.doneData, (_, data) => data);

const $new_message = combine($helloRes, $byeRes, (hello, bye) => `${hello}_${bye}`);

//okay, let's say that it's good. What if I want now to get new byeFx with different query params and use it in another store.

const anotherEvent = createEvent();

forward({
  from: anotherEvent,
  to: byeFx,
});

anotherEvent();

//know all units and samples that depends on byeFx will be called and my app will be crashed.

//we can solve issue with attach. Moreover, attach is not for creating copy of Fx semantically its for passing data from stores.

const byeCopy1Fx = attach({
  effect: byeFx,
  mapParams: params => params,
})

const byeCopy2Fx = attach({
  effect: byeFx,
  mapParams: params => params,
})

//then we need to fix our code 

const $helloRes = createStore('');
const $byeRes = createStore('');
const $someCoolStore = createStore('');
$helloRes.on(helloFx.doneData, (_, data) => data);
$byeRes.on(byeCopy1Fx.doneData, (_, data) => data);
$someCoolStore.on(byeCopy2Fx.doneData, (_, data) => data);

const $new_message = combine($helloRes, $byeRes, (hello, bye) => `${hello}_${bye}`);

const anotherEvent = createEvent();

forward({
  from: anotherEvent,
  to: byeCopy2Fx,
});

forward({
  from: changeEvent,
  to: [helloFx, byeCopy1Fx],
});

someEvent();
anotherEvent();

Can we somehow simplify work with multiple effects?

Use case

olegKusov avatar Dec 09 '21 14:12 olegKusov

Know I found that we can use createEffect for effect copies:

myFx = createEffect(myBaseFx)

All the same overall process for me looks complicated

olegKusov avatar Dec 09 '21 20:12 olegKusov

Looks like you need to use restore and attach with function and without mapParams:

const helloFx = createEffect(async () => {
  return new Promise(resolve => setTimeout(() => resolve('hello'), 3000))
})

const byeFx = createEffect(async () => {
  return new Promise(resolve => setTimeout(() => resolve('bye'), 500))
})

const $processing = patronum.pending({
  effects: [helloFx, byeFx],
})

//we can solve issue with attach. Moreover, attach without mapParams is for creating copy of Fx semantically

/* admins.js */

const sayByeAdmins = createEvent()
const byeAdminsFx = attach({effect: byeFx})
const $adminsMessage = restore(byeAdminsFx, '')

forward({
  from: sayByeAdmins,
  to: byeAdminsFx,
})

/* users.js */

const byeUsersFx = attach({effect: byeFx})
const sayHelloByeUsers = createEvent()

const helloByeUsersFx = attach({
  source: $processing,
  async effect(source, params) {
    const [hello, bye] = await Promise.all([
      helloFx(),
      byeUsersFx(),
    ])
    return `${hello}_${bye}`
  }
})

const $helloByeUsersMessage = restore(helloByeUsersFx, '')

sample({
  clock: sayHelloByeUsers,
  target: helloByeUsersFx,
})

/* app.js */

sayHelloByeUsers()
sayByeAdmins()

I wrote about attach with effect function recently

zerobias avatar Dec 10 '21 00:12 zerobias

I need to think about it. thank you.

olegKusov avatar Dec 13 '21 09:12 olegKusov