2.2.0 • Published 2 years ago

simple-observable-state v2.2.0

Weekly downloads
7
License
ISC
Repository
-
Last release
2 years ago

Simple Observable State

Expressive and boilerplate free state management, with immutability baked in.

Installation

Available on npm

npm install simple-observable-state

What it looks like

// an input with simple-observable-state, with react component, using custom hook useObservableState
function Input({ observableState, ...props }) {
    return <input
        type="text"
        value={observableState.getValue()}
        onChange={e => observableState.next(e.target.value)}
        {...props}
        >
    </input>
}

// using the Input component
function Form() {
    const [formObject, formObservableState] = useObservableState({ username: '', password: '' })
    const usernameObs = useMemo(() => formObservableState.path('username'),[])
    const passwordObs = useMemo(() => formObservableState.path('password'),[])

    return <div>
        {formObject.username} : {formObject.password}
        <form
            onSubmit={e => { e.preventDefault(); console.log(form) }}
        >
            <Input observableState={usernameObs} />
            <Input observableState={passwordObs} />
        </form>
    </div>
}

Get Started

// define initial state
const initial_todos = {
    todos: {
        list: [],
        name: ''
    },
    newTodoInputValue: ''
}

const stored_todos = {
    list: ['foo', 'bar'],
    name: 'My first todo list'
}

// create a new Observable State with initial state value
const todos$ = new ObservableState(initial_todos)

// traverse your observableState property tree
const todosList$ = todos$.path('todos.list')
const newTodo$  = todos$.path('newTodoInputValue')

// set new property on your state
const listName$ = todos$.path('nameTextInputValue').next('January goals:')

// push next state value with observableState.next()
// subscribers will get the new immutable state value automatically, anywhere
function changeInputText(txt){
    newTodo$.next(txt)
}

// provide a state reducer to create the next state value.
function addTodo(){
    // reducer function is called with the current state value
    cons addTodoReducer = function(todos){
        todos.push(todos$.path('newTodoInputText').getValue())
    }
    todosList$.next(addTodoReducer)
}

React & Simple Observable State

Check this WIKI PAGE for a working example of React and SOS working together.

useObservableState custom hook

import {useObservableState} from 'simple-observable-state'

Reference

ObservableState class

extends rxjs.BehaviorSubject

constructor(initial_state, options?)

  • initial_state:any - the initial value for the new ObservableState.
  • options:OptionsObject - an optional object with options to create new ObservableState (used internally - you likely don't need to use any options)

OptionsObject ({id, source?, async?, pluck?, ignoreUndefinedFromSource?})

  • id: string;
  • source?: ObservableState,
  • async?: Promise,
  • pluck?: string[],
  • ignoreUndefinedFromSource?: boolean

observableState.path(path: string | string[])

Returns a new observableState derived from the source observableState.
The new osbservableState emits the value found at path in the source observableState.
path accepts dot notation string or an array of strings :

const state$ = new ObservableState({deep: {value:'hello world'}})
const value$ = state$.path('deep.value')
// is equivalent to 
const value$ = state$.path(['deep','value'])

state$.subscribe(state => console.log('state = ',state))
value$.subscribe(val => console.log('value = ',val))

// $> state = {deep: {value:'hello world'}}
// $> value = "hello world"

Use path with next to modify your state clinically ! Changes deep in the state are automatically picked up by the parent states. Ideal to split your state and delegate deep values to nested components.

const state$ = new ObservableState({deep: {value:'hello world'}})
const value$ = state$.path('deep.value')

state$.subscribe(state => console.log('state =', state))
value$.next('Goodbye world')

// $> state = {deep: {value:'hello world'}}
// $> state = {deep: {value:'Goodbye world'}}

observableState.next(any & StateProducerFunction)

Returns observableState to allow method chaining.
next is used to modify the last state value from osbervableState.
next accepts any value that will be the next emitted state.
next also accepts a function as a parameter, it is called with the last state value.

All changes to the state inside StateProducerFunction will be reflected in the next emitted state. Unless StateProducerFunction returns an object; in which case the next emitted state will be that object.

note: this library uses Immer to create immutable states. StateProducerFunction is essentially an Immer Producer function.

const state$ = new ObservableState({deep: {value:'hello world'}})

state$.next({deep: {value:'XXXxxxXXX'}})
// is equivalent to 
state$.next(function(state){
    state.deep.value = 'XXXxxxXXX'
})

It is possible to chain next calls to modify the state sequentially :

const state$ = new ObservableState(['first'])

state$
    .next(function(state){state.push('second')})
    .next(function(state){state.pop()})
// state = ['first']

observableState.subscribe(subscriber)

Subscribe to ObservableState. Use this function just as you would a regular rxjs.Observable

observableState.value

Inherited from rxjs.BehaviorSubject. Handy to get the current state value

observableState.getValue()

Inherited from rxjs.BehaviorSubject. Handy to get the current state value

Develop

This package is developed with Typescript.

Start watcher to run test suite on code changes :

npm run dev

Build

Builds in dist/ folder.

npm run build

Publish

Publish changes onto npm

npm publish

Test

The library comes with a Jest test suite.

npm run test
<!-- or start dev server to watch file changes -->
npm run dev

Credits

ISC @ Joel Rafalimanana

2.2.0

2 years ago

2.1.8

2 years ago

2.1.2

3 years ago

2.1.1

3 years ago

2.1.4

3 years ago

2.1.3

3 years ago

2.1.6

3 years ago

2.1.5

3 years ago

2.1.7

3 years ago

2.1.0

3 years ago

2.0.14

3 years ago

2.0.13

3 years ago

2.0.11

3 years ago

2.0.12

3 years ago

2.0.7

3 years ago

2.0.9

3 years ago

2.0.10

3 years ago

2.0.8

3 years ago

2.0.3

3 years ago

2.0.2

3 years ago

2.0.5

3 years ago

2.0.4

3 years ago

2.0.6

3 years ago

2.0.1

3 years ago

2.0.0

3 years ago

1.1.0

3 years ago

1.0.8

4 years ago

1.0.7

4 years ago

1.0.6

4 years ago

1.0.5

4 years ago

1.0.4

4 years ago

1.0.3

4 years ago

1.0.2

4 years ago

1.0.1

4 years ago

1.0.0

4 years ago