Socket
Socket
Sign inDemoInstall

mobx-utils

Package Overview
Dependencies
Maintainers
4
Versions
59
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mobx-utils - npm Package Compare versions

Comparing version 6.0.4 to 6.0.5

0

lib/array.d.ts

@@ -0,0 +0,0 @@ import { IObservableArray } from "mobx";

@@ -0,0 +0,0 @@ var __spreadArrays = (this && this.__spreadArrays) || function () {

@@ -0,0 +0,0 @@ import { IDisposer } from "./utils";

@@ -0,0 +0,0 @@ import { isAction, autorun, action, isObservableArray, runInAction } from "mobx";

5

lib/computedFn.d.ts
import { IComputedValueOptions } from "mobx";
export declare type IComputedFnOptions<F extends (...args: any[]) => any> = {
onCleanup?: (result: ReturnType<F> | undefined, ...args: Parameters<F>) => void;
} & IComputedValueOptions<ReturnType<F>>;
/**

@@ -36,2 +39,2 @@ * computedFn takes a function with an arbitrary amount of arguments,

*/
export declare function computedFn<T extends (...args: any[]) => any>(fn: T, keepAliveOrOptions?: IComputedValueOptions<ReturnType<T>> | boolean): T;
export declare function computedFn<T extends (...args: any[]) => any>(fn: T, keepAliveOrOptions?: IComputedFnOptions<T> | boolean): T;

@@ -12,2 +12,9 @@ var __assign = (this && this.__assign) || function () {

};
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};
import { DeepMap } from "./deepMap";

@@ -78,4 +85,5 @@ import { computed, onBecomeUnobserved, _isComputingDerivation, isAction, } from "mobx";

// create new entry
var latestValue;
var c = computed(function () {
return fn.apply(_this, args);
return (latestValue = fn.apply(_this, args));
}, __assign(__assign({}, opts), { name: "computedFn(" + (opts.name || fn.name) + "#" + ++i + ")" }));

@@ -87,2 +95,5 @@ entry.set(c);

d.entry(args).delete();
if (opts.onCleanup)
opts.onCleanup.apply(opts, __spreadArrays([latestValue], args));
latestValue = undefined;
});

@@ -89,0 +100,0 @@ // return current val

@@ -8,9 +8,3 @@ import { IComputedValueOptions } from "mobx";

} & Omit<IComputedValueOptions<B>, "name">;
/**
* Creates a function that maps an object to a view.
* The mapping is memoized.
*
* See: https://mobx.js.org/refguide/create-transformer.html
*/
export declare function createTransformer<A, B>(transformer: ITransformer<A, B>, onCleanup?: (resultObject: B | undefined, sourceObject?: A) => void): ITransformer<A, B>;
export declare function createTransformer<A, B>(transformer: ITransformer<A, B>, arg2?: ITransformerParams<A, B>): ITransformer<A, B>;

@@ -15,2 +15,11 @@ var __assign = (this && this.__assign) || function () {

var memoizationId = 0;
/**
* Creates a function that maps an object to a view.
* The mapping is memoized.
*
* See the [transformer](#createtransformer-in-detail) section for more details.
*
* @param transformer
* @param onCleanup
*/
export function createTransformer(transformer, arg2) {

@@ -17,0 +26,0 @@ invariant(typeof transformer === "function" && transformer.length < 2, "createTransformer expects a function that accepts one argument");

@@ -0,0 +0,0 @@ import { ObservableMap, IComputedValue } from "mobx";

@@ -52,5 +52,9 @@ var __assign = (this && this.__assign) || function () {

invariant(isObservableObject(model), "createViewModel expects an observable object");
var ownMethodsAndProperties = getAllMethodsAndProperties(this);
// use this helper as Object.getOwnPropertyNames doesn't return getters
getAllMethodsAndProperties(model).forEach(function (key) {
var _a;
if (ownMethodsAndProperties.includes(key)) {
return;
}
if (key === $mobx || key === "__mobxDidRunLazyInitializers") {

@@ -57,0 +61,0 @@ return;

@@ -0,0 +0,0 @@ declare type BabelDescriptor = PropertyDescriptor & {

@@ -0,0 +0,0 @@ import { addHiddenProp } from "./utils";

@@ -0,0 +0,0 @@ /**

@@ -0,0 +0,0 @@ /**

@@ -0,0 +0,0 @@ import { IObjectDidChange, IArrayDidChange, IMapDidChange } from "mobx";

@@ -0,0 +0,0 @@ import { observe, isObservableMap, isObservableObject, isObservableArray, values, entries, } from "mobx";

/**
* expr can be used to create temporarily views inside views.
* This can be improved to improve performance if a value changes often, but usually doesn't affect the outcome of an expression.
*
*`expr` can be used to create temporary computed values inside computed values.
* Nesting computed values is useful to create cheap computations in order to prevent expensive computations from needing to run.
* In the following example the expression prevents that a component is rerender _each time_ the selection changes;
* instead it will only rerenders when the current todo is (de)selected.
*
* `expr(func)` is an alias for `computed(func).get()`.
* Please note that the function given to `expr` is evaluated _twice_ in the scenario that the overall expression value changes.
* It is evaluated the first time when any observables it depends on change.
* It is evaluated a second time when a change in its value triggers the outer computed or reaction to evaluate, which recreates and reevaluates the expression.
*
* In the following example, the expression prevents the `TodoView` component from being re-rendered if the selection changes elsewhere.
* Instead, the component will only re-render when the relevant todo is (de)selected, which happens much less frequently.
*
* @example
* const Todo = observer((props) => {
* const todo = props.todo;
* const isSelected = mobxUtils.expr(() => props.viewState.selection === todo);
* const todo = props.todo
* const isSelected = mobxUtils.expr(() => props.viewState.selection === todo)
* const TodoView = observer(({ todo, editorState }) => {
* const isSelected = mobxUtils.expr(() => editorState.selection === todo)
* return <div className={isSelected ? "todo todo-selected" : "todo"}>{todo.title}</div>
* });
*
* })
*/
export declare function expr<T>(expr: () => T): T;
import { computed, _isComputingDerivation } from "mobx";
/**
* expr can be used to create temporarily views inside views.
* This can be improved to improve performance if a value changes often, but usually doesn't affect the outcome of an expression.
*
*`expr` can be used to create temporary computed values inside computed values.
* Nesting computed values is useful to create cheap computations in order to prevent expensive computations from needing to run.
* In the following example the expression prevents that a component is rerender _each time_ the selection changes;
* instead it will only rerenders when the current todo is (de)selected.
*
* `expr(func)` is an alias for `computed(func).get()`.
* Please note that the function given to `expr` is evaluated _twice_ in the scenario that the overall expression value changes.
* It is evaluated the first time when any observables it depends on change.
* It is evaluated a second time when a change in its value triggers the outer computed or reaction to evaluate, which recreates and reevaluates the expression.
*
* In the following example, the expression prevents the `TodoView` component from being re-rendered if the selection changes elsewhere.
* Instead, the component will only re-render when the relevant todo is (de)selected, which happens much less frequently.
*
* @example
* const Todo = observer((props) => {
* const todo = props.todo;
* const isSelected = mobxUtils.expr(() => props.viewState.selection === todo);
* const todo = props.todo
* const isSelected = mobxUtils.expr(() => props.viewState.selection === todo)
* const TodoView = observer(({ todo, editorState }) => {
* const isSelected = mobxUtils.expr(() => editorState.selection === todo)
* return <div className={isSelected ? "todo todo-selected" : "todo"}>{todo.title}</div>
* });
*
* })
*/

@@ -17,0 +25,0 @@ export function expr(expr) {

@@ -0,0 +0,0 @@ export declare type PromiseState = "pending" | "fulfilled" | "rejected";

@@ -0,0 +0,0 @@ import { action, extendObservable } from "mobx";

@@ -0,0 +0,0 @@ import { IDisposer } from "./utils";

@@ -0,0 +0,0 @@ import { createAtom, _allowStateChanges } from "mobx";

@@ -0,0 +0,0 @@ import { IComputedValue } from "mobx";

@@ -0,0 +0,0 @@ import { getAtom, observe } from "mobx";

@@ -0,0 +0,0 @@ export interface ILazyObservable<T> {

@@ -0,0 +0,0 @@ import { observable, action, _allowStateChanges } from "mobx";

@@ -0,0 +0,0 @@ export * from "./from-promise";

@@ -0,0 +0,0 @@ export * from "./from-promise";

@@ -0,0 +0,0 @@ /**

@@ -0,0 +0,0 @@ import { _isComputingDerivation } from "mobx";

@@ -0,0 +0,0 @@ export interface IStreamObserver<T> {

@@ -0,0 +0,0 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {

@@ -0,0 +0,0 @@ import { IObservableArray, ObservableMap } from "mobx";

@@ -0,0 +0,0 @@ var __extends = (this && this.__extends) || (function () {

@@ -0,0 +0,0 @@ import { IDisposer } from "./utils";

@@ -0,0 +0,0 @@ import { isAction, autorun, action, isObservableArray, runInAction } from "mobx";

@@ -0,0 +0,0 @@ export declare type IDisposer = () => void;

@@ -0,0 +0,0 @@ export var NOOP = function () { };

{
"name": "mobx-utils",
"version": "6.0.4",
"version": "6.0.5",
"description": "Utility functions and common patterns for MobX",

@@ -5,0 +5,0 @@ "main": "mobx-utils.umd.js",

@@ -68,7 +68,9 @@ # MobX-utils

- [Examples](#examples-11)
- [createTransformer](#createtransformer)
- [Parameters](#parameters-13)
- [deepObserve](#deepobserve)
- [Parameters](#parameters-13)
- [Parameters](#parameters-14)
- [Examples](#examples-12)
- [ObservableGroupMap](#observablegroupmap)
- [Parameters](#parameters-14)
- [Parameters](#parameters-15)
- [Examples](#examples-13)

@@ -82,3 +84,3 @@ - [ObservableMap](#observablemap)

- [computedFn](#computedfn)
- [Parameters](#parameters-15)
- [Parameters](#parameters-16)
- [Examples](#examples-14)

@@ -538,8 +540,15 @@ - [DeepMapEntry](#deepmapentry)

expr can be used to create temporarily views inside views.
This can be improved to improve performance if a value changes often, but usually doesn't affect the outcome of an expression.
`expr` can be used to create temporary computed values inside computed values.
Nesting computed values is useful to create cheap computations in order to prevent expensive computations from needing to run.
In the following example the expression prevents that a component is rerender _each time_ the selection changes;
instead it will only rerenders when the current todo is (de)selected.
`expr(func)` is an alias for `computed(func).get()`.
Please note that the function given to `expr` is evaluated _twice_ in the scenario that the overall expression value changes.
It is evaluated the first time when any observables it depends on change.
It is evaluated a second time when a change in its value triggers the outer computed or reaction to evaluate, which recreates and reevaluates the expression.
In the following example, the expression prevents the `TodoView` component from being re-rendered if the selection changes elsewhere.
Instead, the component will only re-render when the relevant todo is (de)selected, which happens much less frequently.
### Parameters

@@ -553,8 +562,23 @@

const Todo = observer((props) => {
const todo = props.todo;
const isSelected = mobxUtils.expr(() => props.viewState.selection === todo);
const todo = props.todo
const isSelected = mobxUtils.expr(() => props.viewState.selection === todo)
const TodoView = observer(({ todo, editorState }) => {
const isSelected = mobxUtils.expr(() => editorState.selection === todo)
return <div className={isSelected ? "todo todo-selected" : "todo"}>{todo.title}</div>
});
})
```
## createTransformer
Creates a function that maps an object to a view.
The mapping is memoized.
See the [transformer](#createtransformer-in-detail) section for more details.
### Parameters
- `transformer`
- `arg2`
- `onCleanup`
## deepObserve

@@ -693,1 +717,194 @@

## DeepMap
# Details
## createTransformer in detail
With `createTransformer` it is very easy to transform a complete data graph into another data graph.
Transformation functions can be composed so that you can build a tree using lots of small transformations.
The resulting data graph will never be stale, it will be kept in sync with the source by applying small patches to the result graph.
This makes it very easy to achieve powerful patterns similar to sideways data loading, map-reduce, tracking state history using immutable data structures etc.
`createTransformer` turns a function (that should transform value `A` into another value `B`) into a reactive and memoizing function.
In other words, if the `transformation` function computes B given a specific A, the same B will be returned for all other future invocations of the transformation with the same A.
However, if A changes, the transformation will be re-applied so that B is updated accordingly.
And last but not least, if nobody is using the transformation of a specific A anymore, its entry will be removed from the memoization table.
The optional `onCleanup` function can be used to get a notification when a transformation of an object is no longer needed.
This can be used to dispose resources attached to the result object if needed.
Always use transformations inside a reaction like `observer` or `autorun`.
Transformations will, like any other computed value, fall back to lazy evaluation if not observed by something, which sort of defeats their purpose.
### Parameters
- \`transformation: (value: A) => B
- `onCleanup?: (result: B, value?: A) => void)`
-
`createTransformer<A, B>(transformation: (value: A) => B, onCleanup?: (result: B, value?: A) => void): (value: A) => B`
## Examples
This all might still be a bit vague, so here are two examples that explain this whole idea of transforming one data structure into another by using small, reactive functions:
### Tracking mutable state using immutable, shared data structures.
This example is taken from the [Reactive2015 conference demo](https://github.com/mobxjs/mobx-reactive2015-demo):
```javascript
/*
The store that holds our domain: boxes and arrows
*/
const store = observable({
boxes: [],
arrows: [],
selection: null,
})
/**
Serialize store to json upon each change and push it onto the states list
*/
const states = []
autorun(() => {
states.push(serializeState(store))
})
const serializeState = createTransformer((store) => ({
boxes: store.boxes.map(serializeBox),
arrows: store.arrows.map(serializeArrow),
selection: store.selection ? store.selection.id : null,
}))
const serializeBox = createTransformer((box) => ({ ...box }))
const serializeArrow = createTransformer((arrow) => ({
id: arrow.id,
to: arrow.to.id,
from: arrow.from.id,
}))
```
In this example the state is serialized by composing three different transformation functions.
The autorunner triggers the serialization of the `store` object, which in turn serializes all boxes and arrows.
Let's take closer look at the life of an imaginary example box#3.
1. The first time box#3 is passed by `map` to `serializeBox`,
the serializeBox transformation is executed and an entry containing box#3 and its serialized representation is added to the internal memoization table of `serializeBox`.
2. Imagine that another box is added to the `store.boxes` list.
This would cause the `serializeState` function to re-compute, resulting in a complete remapping of all the boxes.
However, all the invocations of `serializeBox` will now return their old values from the memoization tables since their transformation functions didn't (need to) run again.
3. Secondly, if somebody changes a property of box#3 this will cause the application of the `serializeBox` to box#3 to re-compute, just like any other reactive function in MobX.
Since the transformation will now produce a new Json object based on box#3, all observers of that specific transformation will be forced to run again as well.
That's the `serializeState` transformation in this case.
`serializeState` will now produce a new value in turn and map all the boxes again. But except for box#3, all other boxes will be returned from the memoization table.
4. Finally, if box#3 is removed from `store.boxes`, `serializeState` will compute again.
But since it will no longer be using the application of `serializeBox` to box#3,
that reactive function will go back to non-reactive mode.
This signals the memoization table that the entry can be removed so that it is ready for GC.
So effectively we have achieved state tracking using immutable, shared datas structures here.
All boxes and arrows are mapped and reduced into single state tree.
Each change will result in a new entry in the `states` array, but the different entries will share almost all of their box and arrow representations.
### Transforming a datagraph into another reactive data graph
Instead of returning plain values from a transformation function, it is also possible to return observable objects.
This can be used to transform an observable data graph into a another observable data graph, which can be used to transform... you get the idea.
Here is a small example that encodes a reactive file explorer that will update its representation upon each change.
Data graphs that are built this way will in general react a lot faster and will consist of much more straight-forward code,
compared to derived data graph that are updated using your own code. See the [performance tests](https://github.com/mobxjs/mobx/blob/3ea1f4af20a51a1cb30be3e4a55ec8f964a8c495/test/perf/transform-perf.js#L4) for some examples.
Unlike the previous example, the `transformFolder` will only run once as long as a folder remains visible;
the `DisplayFolder` objects track the associated `Folder` objects themselves.
In the following example all mutations to the `state` graph will be processed automatically.
Some examples:
1. Changing the name of a folder will update its own `path` property and the `path` property of all its descendants.
2. Collapsing a folder will remove all descendant `DisplayFolders` from the tree.
3. Expanding a folder will restore them again.
4. Setting a search filter will remove all nodes that do not match the filter, unless they have a descendant that matches the filter.
5. Etc.
```javascript
import {extendObservable, observable, createTransformer, autorun} from "mobx"
function Folder(parent, name) {
this.parent = parent;
extendObservable(this, {
name: name,
children: observable.shallow([]),
});
}
function DisplayFolder(folder, state) {
this.state = state;
this.folder = folder;
extendObservable(this, {
collapsed: false,
get name() {
return this.folder.name;
},
get isVisible() {
return !this.state.filter || this.name.indexOf(this.state.filter) !== -1 || this.children.some(child => child.isVisible);
},
get children() {
if (this.collapsed)
return [];
return this.folder.children.map(transformFolder).filter(function(child) {
return child.isVisible;
})
},
get path() {
return this.folder.parent === null ? this.name : transformFolder(this.folder.parent).path + "/" + this.name;
})
});
}
var state = observable({
root: new Folder(null, "root"),
filter: null,
displayRoot: null
});
var transformFolder = createTransformer(function (folder) {
return new DisplayFolder(folder, state);
});
// returns list of strings per folder
var stringTransformer = createTransformer(function (displayFolder) {
var path = displayFolder.path;
return path + "\n" +
displayFolder.children.filter(function(child) {
return child.isVisible;
}).map(stringTransformer).join('');
});
function createFolders(parent, recursion) {
if (recursion === 0)
return;
for (var i = 0; i < 3; i++) {
var folder = new Folder(parent, i + '');
parent.children.push(folder);
createFolders(folder, recursion - 1);
}
}
createFolders(state.root, 2); // 3^2
autorun(function() {
state.displayRoot = transformFolder(state.root);
state.text = stringTransformer(state.displayRoot)
console.log(state.text)
});
state.root.name = 'wow'; // change folder name
state.displayRoot.children[1].collapsed = true; // collapse folder
state.filter = "2"; // search
state.filter = null; // unsearch
```

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc