Skip to main content

React Hooks

ℹī¸note

React Hooks are only supported in Functional React Components!

useAgile()​

The useAgile() React Hook binds/subscribes AgileTs States to a Functional React Component for reactivity. This binding ensures that the Component re-renders whenever a bound State changes. We can flexibly bind any Agile Sub Instances (like States or Collections) to any React Component.

const myCoolState = useAgile(MY_COOL_STATE); 

The useAgile() Hook returns the current value of the provided State and not the State Instance itself.

// -- core.js --------------------------------------------------

const MY_STATE = createState('jeff');

// -- MyComponent.jsx ------------------------------------------

const myState = useAgile(MY_STATE);
console.log(myState); // Returns 'jeff'

📚 Array​

We can also pass an array of States to the useAgile() Hook.

useAgile([MY_COOL_STATE1, MY_COOL_STATE2]);

Then useAgile() returns an array of State values matching the specified State Instances. This array can be destructured in order to easily access the individual State values

// -- core.js --------------------------------------------------

const MY_STATE = createState('jeff');
const MY_STATE_2 = createState('frank');

// -- MyComponent.jsx ------------------------------------------

const [myState, myState2] = useAgile([MY_STATE, MY_STATE_2]);
console.log(myState); // Returns 'jeff'
console.log(myState2); // Returns 'frank'

Binding multiple States to a React Component using a single useAgile() Hook has one advantage. It can reduce the number of triggered re-render events by batching re-render jobs. Thereby, simultaneously triggered re-render events of different State Instances are combined into one single (combined) re-render event if these States share the same Subscription Container. Each useAgile() Hook creates its own Subscription Container and registers it with AgileTs. Simply put Subscription Container serve as an interface to React-Components for AgileTs.

👀 Subscribable Instances​

Not only AgileTs States can be bound to React Components via useAgile(), but also all other Agile Sub Instances that contain an Observer.

  const [myCollection, myGroup, myState] = useAgile([MY_COLLECTION, MY_GROUP, MY_STATE]);

Instances that contain an Observer are, for example:

  • State​

    // -- core.js --------------------------------------------------

    const MY_STATE = createState('jeff');

    // -- MyComponent.jsx ------------------------------------------

    const myState = useAgile(MY_STATE);
    console.log(myState); // Returns 'jeff'
  • Computed​

    // -- core.js --------------------------------------------------

    const MY_COMPUTED = createComputed(() => 'hello there');

    // -- MyComponent.jsx ------------------------------------------

    const myComputed = useAgile(MY_COMPUTED);
    console.log(myComputed); // Returns 'hello there'
  • Collection​

    Note: A Collection doesn't contain directly an Observer. But useAgile() is smart enough, to identify a Collection and binds the defualt Group to the React Component instead. The default Group represents the default pattern of the Collection.

    // -- core.js --------------------------------------------------

    const MY_COLLECTION = createCollection({
    initialData: [{id: 1, name: 'a'}, {id: 2, name: 'b'}, {id: 3, name: 'c'}]
    });

    // -- MyComponent.jsx ------------------------------------------

    const myCollection = useAgile(MY_COLLECTION);
    console.log(myCollection); // Returns (see below)
    // '[{id: 1, name: 'a'}, {id: 2, name: 'b'}, {id: 3, name: 'c'}]'
  • Group​

    // -- core.js --------------------------------------------------

    const MY_COLLECTION = createCollection({
    initialData: [{id: 1, name: 'a'}, {id: 2, name: 'b'}, {id: 3, name: 'c'}]
    });
    const MY_GROUP = MY_COLLECTION.createGroup('myGroup', [3, 1]);

    // -- MyComponent.jsx ------------------------------------------

    const myGroup = useAgile(MY_GROUP);
    console.log(myGroup); // Returns '[{id: 3, name: 'c'}, {id: 1, name: 'a'}]'
  • Selector​

    // -- core.js --------------------------------------------------

    const MY_COLLECTION = createCollection({
    initialData: [{id: 1, name: 'a'}, {id: 2, name: 'b'}, {id: 3, name: 'c'}]
    });
    const MY_SELECTOR = MY_COLLECTION.select(2);

    // -- MyComponent.jsx ------------------------------------------

    const mySelector = useAgile(MY_SELECTOR);
    console.log(mySelector); // Returns '{id: 2, name: 'b'}'
  • Item​

    // -- core.js --------------------------------------------------

    const MY_COLLECTION = createCollection({
    initialData: [{id: 1, name: 'a'}, {id: 2, name: 'b'}, {id: 3, name: 'c'}]
    });
    const MY_ITEM = MY_COLLECTION.getItem(3);

    // -- MyComponent.jsx ------------------------------------------

    const myItem = useAgile(MY_ITEM);
    console.log(myItem); // Returns '{id: 3, name: 'c'}'

🔴 Example​

Live Editor
Result

đŸŸĻ Typescript​

The useAgile() Hook is almost 100% typesafe.

// -- core.js --------------------------------------------------

const NUMBER_STATE = createState(0);
const STRING_STATE = createState('hello there');

// -- MyComponent.jsx ------------------------------------------

const [numberState, stringState] = useAgile([NUMBER_STATE, STRING_STATE]);
console.log(typeof numberState); // Returns 'number'
console.log(typeof stringState); // Returns 'string'

📭 Props​

PropTypeDescriptionRequired
depsArray<SubscribableAgileInstancesType> | SubscribableAgileInstancesTypeAgile Sub Instances to be bound to the Functional Component.Yes
configAgileHookConfigInterfaceConfigurationNo

SubscribableAgileInstancesType​

type SubscribableAgileInstancesType = State | Collection | Observer | undefined;

📄 Return​

The useAgile() Hook returns the current output or if the Instance has no output the current value of the specified Agile Sub Instance.

// -- core.js -------------------------------------------------

const MY_STATE = createState('jeff');

// -- MyComponent.jsx ------------------------------------------

const myState = useAgile(MY_STATE);
console.log(myState); // Returns 'jeff'

When passing multiple Agile Sub Instances, an array of outputs/values matching the passed Instances is returned.

// -- core.js --------------------------------------------------

const MY_STATE = createState('jeff');
const MY_STATE_2 = createState('frank');

// -- MyComponent.jsx ------------------------------------------

const [myState, myState2] = useAgile([MY_STATE, MY_STATE_2]);
console.log(myState); // Returns 'jeff'
console.log(myState2); // Returns 'frank'



useProxy()​

đŸ”Ĩwarning

Requires an additional package called @agile-ts/proxytree!

The useProxy() is in its basic functionality equivalent to the useAgile() Hook. It binds/subscribes AgileTs States to a Functional React Component for reactivity. However, it has one advantage in terms of performance. Because it only re-renders the React Component when an actual accessed property changes. This is accomplished by warping a Proxy() around the returned State value object/s. Through this Proxy, AgileTs is able to exactly track accessed properties in the React Component and can construct paths to these. Based on these paths, it can select the particular accessed properties.

With the useAgile() Hook, the Component would always be re-rendered on a subscribed State value change, regardless of whether the changed property value was accessed in the Component or not.

👀 Subscribable Instances​

Not only AgileTs States can be bound to React Components via useProxy(), but also all other Agile Sub Instances that contain an Observer.

  const [myCollection, myGroup, myState] = useProxy([MY_COLLECTION, MY_GROUP, MY_STATE]);

However, a Javascript Proxy can only be wrapped around values of the type object. Instances that are not of the type object are treated as in useAgile().

🔴 Example​

Live Editor
Result

đŸŸĻ Typescript​

The useProxy() Hook is almost 100% typesafe.

📭 Props​

PropTypeDescriptionRequired
depsArray<SubscribableAgileInstancesType> | SubscribableAgileInstancesTypeAgile Sub Instances to be bound to the Functional Component.Yes
configAgileHookConfigInterfaceConfigurationNo

SubscribableAgileInstancesType​

type SubscribableAgileInstancesType = State | Collection | Observer | undefined;

📄 Return​

The useProxy() Hook returns the current output or if the Instance has no output the current value of the specified Agile Sub Instance.

// -- core.js -------------------------------------------------

const MY_STATE = createState('jeff');

// -- MyComponent.jsx ------------------------------------------

const myState = useProxy(MY_STATE);
console.log(myState); // Returns 'jeff'

When passing multiple Agile Sub Instances, an array of outputs/values matching the passed Instances is returned.

// -- core.js --------------------------------------------------

const MY_STATE = createState({id: 1: name: 'jeff'});
const MY_STATE_2 = createState('frank');

// -- MyComponent.jsx ------------------------------------------

const [myState, myState2] = useProxy([MY_STATE, MY_STATE_2]);
console.log(myState); // Returns '{id: 1: name: 'jeff'}'
console.log(myState2); // Returns 'frank'



useSelector()​

The useSelector() React Hook binds/subscribes a part of an AgileTs State to a Functional React Component for reactivity. This binding ensures that the Component re-renders whenever the bound State part/property changes. The to bind part is selected via a selector function specified as second parameter.

const myName = useAgile(MY_USER, (v) => v.name); 

👀 Subscribable Instances​

Not only parts of AgileTs States can be bound to React Components via useSelector(), but also parts of all other Agile Sub Instances that contain an Observer.

const myItem1 = useSelector(MY_COLLECTION, (v) => v['item1']);

🔴 Example​

Live Editor
Result

đŸŸĻ Typescript​

The useSelector() Hook isn't completely typesafe yet.

const selectedValue = useSelector(MY_STATE, (v) => v.name);

We are still figuring out how to automatically detect and return the selected property type. As an override, you can also specify the individual types as generics.

useSelector<typeof MY_STATE.value, string>(MY_STATE, (v) => v.name);

Or explicitly assign the desired type to the return value with the as keyword.

useSelector(MY_STATE, (v) => v.name) as string;

📭 Props​

PropTypeDescriptionRequired
depSubscribableAgileInstancesTypeAgile Sub Instance to be passed into the specified selector method.Yes
selectorSelectorMethodTypeSelector method to select the part/property of the specified Agile Sub Instance value to be bound to the Functional Component.Yes
configAgileHookConfigInterfaceConfigurationNo

SubscribableAgileInstancesType​

type SubscribableAgileInstancesType = State | Collection | Observer | undefined;

SelectorMethodType​

type SelectorMethodType<T = any> = (value: T) => any;

📄 Return​

The useSelector() Hook returns the selected property (based on the selector method) of the specified Agile Sub Instance.

// -- core.js -------------------------------------------------

const MY_STATE = createState({id: 10, name: 'jeff', age: 10});

// -- MyComponent.jsx ------------------------------------------

const myName = useSelector(MY_STATE, (v) => v.name);
console.log(myName); // Returns 'jeff'



useValue()​

The useValue() is in its basic functionality equivalent to the useAgile() Hook. It binds/subscribes AgileTs States to a Functional React Component for reactivity. However, it differs in on key area, because it explicitly binds the value of a State or other Agile Sub Instances to the Component instead of preferring the ouptut. Normally (like in the useAgile() Hook), the output of an Agile Sub Instance has a higher weight than the value.

For example if we bind a Collection with the useAgile() Hook to a React Component, the output of the Collection is bound to the Component. When we use the useValue() Hook instead, the value of the Collection is bound to the Component.

// -- core.js -------------------------------------------------

const MY_COLLECTION = createCollection({
initialData: [{id: 1, name: 'a'}, {id: 2, name: 'b'}, {id: 3, name: 'c'}]
});

// -- MyComponent.jsx ------------------------------------------

const collectionValue = useValue(MY_COLLECTION);
console.log(collectionValue); // Returns '[1, 2, 3]'

const collectionOutput = useAgile(MY_COLLECTION);
console.log(collectionOutput); // Returns (see below)
// '[{id: 1, name: 'a'}, {id: 2, name: 'b'}, {id: 3, name: 'c'}]'

👀 Subscribable Instances​

Not only AgileTs States can be bound to React Components via useValue(), but also all other Agile Sub Instances that contain an Observer.

  const [myCollection, myGroup, myState] = useValue([MY_COLLECTION, MY_GROUP, MY_STATE]);

🔴 Example​

Live Editor
Result

đŸŸĻ Typescript​

The useValue() Hook is almost 100% typesafe.

📭 Props​

PropTypeDescriptionRequired
depsArray<SubscribableAgileInstancesType> | SubscribableAgileInstancesTypeAgile Sub Instances to be bound to the Functional Component.Yes
configAgileHookConfigInterfaceConfigurationNo

SubscribableAgileInstancesType​

type SubscribableAgileInstancesType = State | Collection | Observer | undefined;

📄 Return​

The useValue() Hook returns the current value of the specified Agile Sub Instance.

// -- core.js -------------------------------------------------

const MY_COLLECTION = createCollection({initialData: [
{id: 1, name: 'jeff'}, {id: 2, name: 'hans'}
]});

// -- MyComponent.jsx ------------------------------------------

const myValue = useValue(MY_COLLECTION);
console.log(myValue); // Returns '[1, 2]'

When passing multiple Agile Sub Instances, an array of values matching the passed Instances is returned.

// -- core.js --------------------------------------------------

const MY_COLLECTION = createCollection({initialData: [
{id: 1, name: 'jeff'}, {id: 2, name: 'hans'}
]});
const MY_STATE_2 = createState('frank');

// -- MyComponent.jsx ------------------------------------------

const [myValue, myState2] = useValue([MY_COLLECTION, MY_STATE_2]);
console.log(myValue); // Returns '[1, 2]'
console.log(myState2); // Returns 'frank'



useWatcher()​

The useWatcher() React Hook lets us easily observe a State for changes. Thereby is the provided callback function fired on every State value mutation. Such mutation occurs when we, for example, update the State value from 'jeff' to 'hans'.

useWatcher(MY_STATE, (value) => {
console.log(value); // Returns current State Value
});

The useWatcher() Hook is a synonym to the watch() method. However, it has some advantages within React Components:

  • It automatically cleans up the created watcher callback when the React Component unmounts
  • Is nicer to read in 'UI-Component-Code'

🔴 Example​

Live Editor
Result

đŸŸĻ Typescript​

The useWatcher() Hook is almost 100% typesafe.

📭 Props​

PropTypeDescriptionRequired
stateState<T>State to which the specified watcher callback belongs.Yes
callbackStateWatcherCallback<T>A function to be executed on each State value change.Yes

📄 Return​

void