Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/effector/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2999,15 +2999,15 @@ export function serialize(
* @param unit event to bind
* @returns function which will trigger an event in a given scope
*/
export function scopeBind<T>(unit: Event<T>, opts?: {scope: Scope}): (payload: T) => void
export function scopeBind<T>(unit: Event<T>, opts?: {scope?: Scope; safe?: boolean}): (payload: T) => void
/**
* Bind effect to a scope to be called later.
*
* When `scope` is not provided this method retrieve scope implicitly from scope of the handler (effect handler or watch function) inside which it's being called
* @param unit effect to bind
* @returns function which will trigger an effect in a given scope and returns a promise with a result
*/
export function scopeBind<P, D>(unit: Effect<P, D>, opts?: {scope: Scope}): (params: P) => Promise<D>
export function scopeBind<P, D>(unit: Effect<P, D>, opts?: {scope?: Scope; safe?: boolean}): (params: P) => Promise<D>

/**
* Creates isolated instance of application. Primary purposes of this method are SSR and testing.
Expand Down
43 changes: 43 additions & 0 deletions src/effector/__tests__/fork/scopeBind.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,46 @@ it('returns promise when used with effect', async () => {
const req = scopeFx()
expect(req instanceof Promise).toBe(true)
})

it('does not throw on calls without scope, if called in safe mode', () => {
const trigger = createEvent()

expect(() => {
scopeBind(trigger, {safe: true})
}).not.toThrow()
})

it('allows calls without scope, if called in safe mode', async () => {
const trigger = createEvent()
const inc = createEvent()
const $count = createStore(0).on(inc, x => x + 1)

let fn: () => void

trigger.watch(() => {
fn = scopeBind(inc, {safe: true})
})

trigger()
await Promise.resolve()
fn!()
expect($count.getState()).toBe(1)
})

it('catches scope, if called with scope in safe mode', async () => {
const trigger = createEvent()
const inc = createEvent()
const $count = createStore(0).on(inc, x => x + 1)

let fn: () => void

trigger.watch(() => {
fn = scopeBind(inc, {safe: true})
})

const scope = fork()
await allSettled(trigger, {scope})
fn!()
expect(scope.getState($count)).toBe(1)
expect($count.getState()).toBe(0)
})
7 changes: 5 additions & 2 deletions src/effector/fork/scopeBind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ import type {Scope} from '../unit.h'
import type {Unit} from '../index.h'

/** bind event to scope */
export function scopeBind(unit: Unit, {scope}: {scope?: Scope} = {}) {
export function scopeBind(
unit: Unit,
{scope, safe}: {scope?: Scope; safe?: true} = {},
) {
assert(
scope || forkPage,
scope || forkPage || safe,
'scopeBind cannot be called outside of forked .watch',
)
const savedForkPage = scope || forkPage!
Expand Down