1.0.4 • Published 6 years ago

context-scope v1.0.4

Weekly downloads
2
License
MIT
Repository
github
Last release
6 years ago

context-scope

Context support for Javascript / Typescript. Side effects free. Create a scope and all functions called from within it will have access to it. Use multiple context's at the same time with inherited scopes.

Getting started

Example

import Context from 'context-scope'

interface User {
  name: string
  surname: string
  email: string
}

const user = new Context<User>()

const fullName = () => `${user.value.name} ${user.value.surname}`
const link = () => `mailto:${user.value.email}`
const title = () => `${fullName()} [${user.value.email}]`

const getHTML = () => `<a href="${link()}" title="${title()}">${fullName()}</a>`

user.scope(scope => {
  scope.set({
    name: 'Rick',
    surname: 'Astley',
    email: 'hello@example.com'
  })

  const link = getHTML()

  console.log(link)
  // <a href="mailto:hello@example.com" title="Rick Astley [hello@example.com]">Rick Astley</a>
})

How

By using a dynamically generated function and stack traces, it's possible to make the code context-aware. Each scope is given an identifier and bound to a function with it in the name.

From there, it's as simple as parsing the stack trace and extracting the identifiers inside a getter.

Why

You often find the need to pass data from one place to another. This would normally mean passing down the data via parameters, but this quickly becomes unmaintainable. Context allows you to skip all the hops and pass it directly where you need it.

This comes with some added benefits, for example - Typescript. As you're only defining your data in one place, you only need to type it once. The rest is done for you.

Options

initialValue

Defines the initial value to be used for all newly created scopes. It's recommended to directly call scope.set from within a scope, as it provides safety in strictMode when destroying contexts.

customHandlers

Handlers are methods defined on the scope, which take the current context + arguments and return a new version of the data.

const context = new Context<number>({
  customHandlers: {
    increment: value => (amount = 1) => +value + amount,
    decrement: value => (amount = 1) => +value - amount
  }
})

context.scope(scope => {
  scope.set(0)

  scope.increment()
  console.log(scope) // 1

  scope.decrement(2)
  console.log(scope) // -1
})

strictMode

Strict mode is enabled by default as it provides extra runtime context safety. When it's enabled, it throws errors if context is consumed but unable to be resolved in the stack trace.

On:

const context = new Context()
context.value // Throws error

Off:

const context = new Context()
context.value // undefined

Context methods

destroy

Destroys a specified scope ID from memory

context.scope(scope => {
  scope.set(1)
  const { id } = scope

  context.destroy(id)
})

getScopes

Returns an array of all the parent scope IDs from which the function was called

context.scope(scope => {
  context.scopes() // [ 0 ]
})

value

Returns the value of the current scope from the stack trace

context.scope(scope => {
  scope.set(1)

  context.value // 1
})

getScope

Returns the value of a scope ID

context.scope(scope => {
  scope.set('hello')
})

context.getScope(0) // { value: 'hello', initialized: 'true' }

Scope methods

set

context.scope(scope => {
  // Set the value
  scope.set(1)

  // Increment the value based on previous
  scope.set(value => value + 1)
})

value

Returns the direct value of the initialized scope, without parsing the stack trace.

context.scope(scope => {
  scope.set(1)
  scope.value // 1
})

destroy

Destroy's the scope references from memory. If you attempt to read a destroyed scope when strictMode is enabled, an error will be thrown. If you disable strictMode, then it will fallback to undestroyed parent scopes.

context.scope(scope => {
  scope.set(1)
  scope.destroy()

  context.value // error
})

id

Returns the internal ID to the current scope

context.scope(scope => {
  scope.set(1)
  const { id } = scope // 0

  context.destroy(id)
})

Examples

Inheriting values

import Context from 'context-scope'

const context = new Context<number>()

function b() {
  console.log(context.value)
}

function a() {
  context.scope(scope => {
    scope.set(value => (value + 1))
    b()
  })
}

context.scope(scope => {
  scope.set(0)

  console.log(context.value) // 0

  a() // 1

  b() // 0
})

Multiple contexts

import Context from 'context-scope'

const cat = new Context<string>()
const dog = new Context<string>()

function test() {
  console.log(cat.value, dog.value) // 'Cat' 'Dog'
}

cat.scope(feline => {
  feline.set('Cat')
  dog.scope(doggie => {
    doggie.set('Dog')
    test()
  })
})

Return values from scopes

import Context from 'context-scope'

const context = new Context<string>()

async function main() {
  const result = await context.scope(scope => new Promise((resolve) => {
    scope.set('test')
    setTimeout(() => resolve(scope.value), 1000)
  }))

  console.log(result) // 'test'
}

main()
1.0.4

6 years ago

1.0.3

6 years ago

1.0.2

6 years ago

1.0.1

6 years ago

1.0.0

6 years ago