npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@linked/value

v3.1.3

Published

ValueLink class representing an abstract reference to the object's property

Downloads

1,163

Readme

ValueLink Class

Create link

static Link.value( value, nextValue => void ) : Link

Create custom link with the given value and update function.

It may be used for different scenarios. Good example is to use 'edit element' component for adding new element.

Imagine that we have a component <EditUser valueLink={ userLink } /> expecting the link to an object. When editing is finished, EditUser will update the given link with a new values.

Then, following custom link will allow you to add new user with the same form element.

<EditUser valueLink={ Link.value( {}, x => userArrayLink.push( x ) ) } />

Read more about links to objects updates in the next section.

static Link.mutable( object ) : Link

Create the link to a mutable object. Useful for testing and linking observable state.

const state = { a : 1 },
    $state = Link.mutable( state );

$state.at( 'a' ).set( 2 );
expect( state.a ).toBe( 2 );

Read more about links to objects updates in the next section.

static Link.getValues({ [ name ] : Link }) : { [ name ] : value }

Extracts an object with link values. Leading $ is removed from property names in a result.

export const MyCoolComponent = ( props ) => {
    const $name = useLink( 'a' ),
          $email = useLink( 'b' ),

    ...
    const values = Link.getValues({ $name, $email });
    console.log( values ); // { name : 'a', email : 'b' }
}
static Link.getErrors({ [ name ] : Link }) : { [ name ] : value }

Extracts link validation errors. Returns an empty object if there are no errors. Leading $ is removed from property names in a result.

export const MyCoolComponent = ( props ) => {
    const $name = useLink( 'a' ),
          $email = useLink( 'b' ),

    ...
    const values = Link.getErrors({ $name, $email });
    console.log( values ); // { name : 'a', email : 'b' }
}
static Link.setValues({ [ name ] : Link }) : void

Bulk set links from the object with values. Values object must not contain the leading $ in names.

export const MyCoolComponent = ( props ) => {
    const $name = useLink( 'a' ),
          $email = useLink( 'b' ),

    ...
    // Somewhere on I/O completion:
    Link.setValues({ $name, $email }, json);
}

Links to object and arrays

method $object.at( key ) : Link

Create link to the member of array or object.

If linked value is plain object or array, it's possible to generate links to their members. Whenever this derivative links will be updated, it will lead to proper purely functional update (with shallow copying) of the parent element.

const $name = this.$at( 'array' ).at( 0 ).at( 'name' );
$name.set( 'Joe' ); // Will update component state.array
method $object.pick( key1, key2, ... ) : { [ key ] : Link }

Create links to the object's members, and wrap them in an object. When no arguments are provided, it link all object's properties.

const user$ = $user.pick( 'name', 'email' ),
      { name, email } = user$;
method $objOrArray.map( ( $item, itemKey ) => any | void ) : any[]

Map and filter through array or object.

var list = $stringArray.map( ( $item, index ) => {
    if( $item.value ){ // Skip empty elements
        return (
            <div key={ index }>
                <Input $value={ $item } />
            </div>
        );
    }
});

Bind to control

var $something.props : { value, onChange }

Bind link to the standard form control consuming value and onChange props.

<input {...$something.props} />

Custom data-bound controls

You're encouraged to create your own semantic form controls to take the full advantage of the value links features. An example of the control:

const Input = ({ $value, ...props }) => (
    <div className={`form-control ${ $value.error ? 'error' : '' }`}>
        <input {...props}
            value={ $value.value }
            onChange={ e => $value.set( e.target.value ) }
        />
        <div className="validation-error">{ $value.error || '' }</div>
    </div>
);

Offhand boolean links

method $array.contains( element ) : Link

Creates the link to the presence of value in array.

Resulting link value is true whenever element is present in array, and false otherwise. Whenever resulting link is assigned with new value, it will flip element in the array.

Useful for the large checkbox groups.

const optionXBoolLink = arrayLink.contains( 'optionX' );
method linkToAny.equals( whenTrue ) : Link

Create boolean link to value equality.

Resulting link value is true whenever parent link value equals to whenTrue, and false otherwise. When resulting link is assigned with true, it sets parent link value with whenTrue, and with null otherwise.

Useful for radio groups.

const optionXLink = stringLink.equals( 'optionX' );
method linkToAny.enabled( defaultValue = '' ) : Link

Create boolean link which value is false when parent link is null (or undefined), and true otherwise. Whenever the enabled-link is set to true, it sets parent link to the defaultValue.

This type of links is used to support enabling/disabling of individual form controls with a dedicated checkbox. <Input> control and the rest of form controls must be modified to disable themselves when its valueLink.value === null.

const textLink = this.linkAt( 'text' );

return (
    <Checkbox checkedLink={ textLink.enabled() } />
    <Input valueLink={ textLink } /> 
);

Custom links

method link.onChange( callback : any => void ) : Link

Create the wrapper for existing link which will invoke callback whenever new value is set. Similar to:

Link.value( link.value, x => {
    callback( x );
    link.set( x );
});
method link.pipe( transform : ( next, prev ) => any ) : Link

Create the wrapper for existing link which will invoke given transform function before new value is set. Returned value will be used as new link value, and if it's undefined update will be rejected. Similar to:

Link.value( link.value, x => {
    const y = callback( x, link.value );
    if( y !== undefined ){
        link.set( y );
    }
});

Usage example:

<Input valueLink={ strLink.pipe( x => x && x.toUpperCase() ) }/>

Note for TypeScript users

Link is the parametric type Link< T >, where T is the type of the enclosed value.

TypeScript properly infers type of the link and perform static checks failing on missing state members.

interface MyState {
    name : string
}
...
const nameLink = this.linkAt( 'name' ); // Link< string >
const missingLink = this.linkAt( 'age' ); // Compile-time error - no such a member in state.

Link updates

Simple value updates

method link.set( x ) : void
method link.requestChange( x ) : void

Set link to the given value.

<button onClick={ () => boolLink.set( !boolLink.value ) } />
method link.update( prevValue => any ) : void

Update link value using the given value transform function.

<button onClick={ () => boolLink.update( x => !x ) } />
method link.action( ( prevValue, event ) => any ) : ( event => void )

Create UI event handler which will transform the link.

link.action takes transform function, and produce a new function which takes single event argument. When it's called, event and link value are passes as transform parameters, and link will be updated with returned value.

This is particularly useful in (but not restricted to) UI event handlers.

// simple click event handler...
<button onClick={ boolLink.action( x => !x ) } />

// manual binding to input control:
const setValue = ( x, e ) => e.target.value;
...
<input  value={ link.value }
        onChange={ link.action( setValue ) } />

Link to objects and arrays updates

Plain objects and arrays are shallow copied by link.update() and within link.action() handlers, thus it's safe just to update the value in place.

method $object.update( clonedObject => Object ) : void

Update enclosed object or array.

method $object.action( ( clonedObject, event ) => Object ) : ( event => void )

Creates action to update enclosed object or array. Object is shallow copied before the update and it's safe to

<button onClick={ () => $object.update( obj => {
                                obj.a = 1;
                                return obj;
                            }) } />
method $object.removeAt( key ) : void
method $object.at( key ).remove() : void

Remove element with a given key from the enclosed object ar array.

Link to arrays updates

Link to arrays proxies some important Array methods.

method $array.splice( ... ) : void
method $array.push( ... ) : void
method $array.unshift( ... ) : void

Works in the same way and accepts the same parameters as corresponding Array method, but returns undefined and leads to the proper purely functional update of the parent object chain.

Links validation

It's highly recommended to read tutorial on validation with value links.

method $link.check( value => boolean, error = 'Invalid value' ) : Link

Evaluate given condition for the current link value, and assign given error object to the link.error when it fails. There are no restriction on the error object shape and type.

It's possible to assign default error message to the validator function. linked-controls package provides isRequired and isEmail generic validator functions as an examples:

export const isRequired = x => x != null && x !== '';
isRequired.error = 'Required';

Checks can be chained. In this case, the first check which fails will leave its error in the link.

var $link.error : any | void

This field is populated by the link.check method and must not be assigned manually. It should be used by a custom <Input /> control to display an error (see linked-controls and examples).

// Simple check
const $num = this.$at( 'num' )
                .check( x => x >= 0 && x <=5 );

console.log( $num.error );

// Check with error message
const $num = this.$at( 'num' )
                .check( x => x >= 0 && x <=5, 'Number must be between 0 and 5' );

console.log( $num.error );

// Chained checks
const $num = this.$at( 'num' )
                .check( x => x >= 0, 'Negative numbers are not allowed' )
                .check( x => x <= 5, 'Number should be not greater than 5' );

console.log( $num.error );