realar-project / realar Goto Github PK
View Code? Open in Web Editor NEW5 kB Advanced state manager for React
License: MIT License
5 kB Advanced state manager for React
License: MIT License
sharedLocale().lang.flow.to(formattedTextOverStyles, (curr_code, curr_styles?, prev_code?) => {
const data = getInstructionData() as any;
return data['styles_' + code] || {};
});
// No use `on` inside, use only `flow`
// No possible because flow not used subscription, its necessary for making flow body, not a flow intersections
const to = (src, target, fn) => {
const [get] = flow((stop, prev_code) => {
fn(src.val, target.val, prev_code)
});
}
// Hmmmmmm, but `on` impl: ->
const to = (src, target, fn) => {
on(src, (v, prev_v) => {
target.set(fn(v, target.get(), prev_v))
});
const [get] = flow((stop, prev_code) => {
fn(src.val, target.val, prev_code)
});
}
// And update reactive-box API:
sell((prev_value?) => {
});
flow(init_, (stop?, prev_value?) => {
});
const r = ready.resolved(10);
test('should work signal combine', async () => {
const spy = jest.fn();
const v = value(1);
const s = signal.from(v.select(v => v + v));
// `signal.from` should be a stoppable selector, not a new signal, with a change argument subscription.
// `flow` - is a stoppable selector (maybe simple selector is enough, and make another way to create stopping for flow)
// for example `flow.filter`, and `wrap.filter` for making stoppable flow, maybe its enough. Because breaking flow a change type to `Ensurable`
// but stoppable selector is very very interesting - is the possibility to stop flow, without the creation of new `on` subscription.
// simplest way for `sel` and `on` - is making stop exception.
const s = sel(() => {
if (v.val > 0) stop(); // This will be next exported function from reactive-box
// It is a special exception throwing
});
// It's possible to use in another case:
on(() => {
if (v.val > 0) stop(); // <-- its will work automatically)) because `on` used `sel` inside.
}, fn);
// ===
const c = signal.combine(v, s);
c.watch(v => spy(v));
expect(c.val).toEqual([1, 2]);
s(10);
s(10);
v(2);
v(2);
expect(spy).toHaveBeenNthCalledWith(1, [1, 10]);
expect(spy).toHaveBeenNthCalledWith(2, [1, 10]);
expect(spy).toHaveBeenNthCalledWith(3, [2, 10]); // Todo: hmm
expect(spy).toHaveBeenNthCalledWith(4, [2, 4]);
expect(spy).toBeCalledTimes(4);
});
[] Improve cycle
to async operations and yield
notation.
cycle(async ([isCancelled]) => {
const data = await action;
if (isCancelled()) return;
// ...
});
cycle(function* (cancel) {
const data = yield action;
// ...
});
Article Preact + Realar = ~9kB min gzip. All whats you need!
const { signal, loop } = require('realar');
const a = signal();
const b = signal();
const c = signal();
loop(async () => {
await a; console.log('a ready');
await b; console.log('b ready');
await c; console.log('c ready');
// possible syntax
// const [pa, pb, pc] = [a.promise, b.promise, c.promise];
// await pa;
// await pb;
// await pc;
// console.log('ready');
});
a();
b();
c();
https://runkit.com/betula/60572b6307e748001a619912
Or maybe make another type of call
a.safe();
b.safe();
c.safe();
No... first suggest was better...
const TickerService = unit({
current: 0,
get next() {
return this.current + 1;
},
tick() {
this.current += 1;
},
constructor() {},
destructor() {}
});
const ticker = new TickerService();
ticker.tick();
console.log(ticker.next);
function action(fn) {
return fn;
}
function computed(fn) {
return fn;
}
function setting() {}
function getting() {}
function unit(schema) {
const source = Object.getOwnPropertyDescriptors(schema);
const DataKey = "data";
const descriptors = {};
Object.keys(source).forEach(key => {
const { value, get, set } = source[key];
const descriptor = {};
if (typeof value === "function") {
descriptor.value = action(value);
} else if (get) {
descriptor.get = computed(get);
if (set) {
descriptor.set = action(set);
}
} else {
descriptor.set = function(val) {
(this[DataKey] = this[DataKey] || {})[key] = val;
setting();
};
descriptor.get = function() {
const val = (this[DataKey] = this[DataKey] || {})[key] || value;
getting();
return val;
};
}
descriptors[key] = descriptor;
});
let constructor = source.constructor && source.constructor.value;
if (constructor) {
constructor = action(constructor);
}
const Class = function() {
if (constructor) {
constructor.apply(this, arguments);
}
};
Class.prototype = Object.defineProperties({}, descriptors);
return Class;
}
https://codesandbox.io/s/elastic-fermi-or16g?file=/src/index.js:0-1406
class A {
@prop m;
constructor() {
k_signal.watch(k => this.m = k);
}
}
k_signal.view(k => new A());
let t;
(cycle and loop)(() => {
(await )k_signal;
t = new A();
});
Automatic unsubscribe k_signal.watch
... When? At the next iteration, but
loop(() => {
if (await k_signal) {
t = new A();
}
});
In that case, automatic destroy should be not on the next iteration.
Add stop method to stop signal. (and export stop signal from library optional)
stoppable().stop();
// Or may be
stoppable.stop(); // Think about that syntax as a shortcut for the previous one
const v = value(0);
const readonly_v = v.readonly();
// v: signal|value|ready|etc...
v.flow((dest, val, prev?) => val >= 0 && dest(val)); // That case is new one.
// That necessary for creating a new entity (signal, value, ready, etc...), from another entity
const v = value(0);
const positive_v = v.view((n) => n >= 0 ? n : stoppable().stop()); // Add stop method to stop signal. (Make external stop siglal interface)
on(positive_v, console.log) // only positive reactions
// but inside value can be any values, and negative too.
// Task: make a positive stream, ignore negative values.
const v = value(0);
const b = value(0);
sync(v, (val) => val >= 0 && b(val));
// what is the method (shortcut) for making it
v.view((dest, val) => val >= 0 && dest(val));
// This is the perfect view.
// or
v.view((val) => val < 0 && stoppable.stop());
// No have reasons for making stoppable in view. Because it will stop only subscribers reaction, but what to be received for direct reading? (undefined)
// That method provide a new entity with the same set of methods.
// But Its only view. Its parts of one entity. Its not a one task.
v.flow((dest, val, prev?) => val >= 0 && dest(val)); // That case is new one.
// That necessary for creating a new entity (signal, value, ready, etc...), from another entity
realar-rehook
or
realar-rehooks
This module provides the possibility for reusing standard and custom React hooks. One function for creating a container with use-between hooks implementation inside.
import { useState, useEffect } from "react";
import { value, useLocal} from "realar";
import { rehook } from "realar-rehook';
class Logic {
a = value("");
b = rehook(() => {
const [name, setName] = useState("");
// And possible to realar logic here
useEffect(() => (
this.a.watch(setName)
), []);
useEffect(() => this.a.set(name), [name]);
// The useEffect should be run synchronously because we don't have mount/unmount component phase
return {
name,
setName
}
});
constructor() {
this.b.val.setName("Joe");
}
}
const Comp = () => {
const logic = useLocal(Logic);
return (
<p>{logic.b.val.name} - {logic.a.val}</p>
)
}
And I can implement React compatibility mode
const hooks = rehook.compat(() => {
console.log(1);
useEffect(() => console.log(3), []);
console.log(2);
// And support the useLayoutEffect as alias for useEffect
});
Because possible to using the only index accessing and destructing during variable definition.
const [get, set] = signal(0);
// or
const get = v[0], set = v[1];
But not a signal(0).length
or signal(0).slice(1)()
The loop
will be removed because serial of async operation not compatible with serial of sync operations.
This task started from the idea and hypothesis possible syntax:
const info = pool(async () => {
return await search(...)
});
// info.proc - parallel process count
// info.pending - proc > 0
// info.stop() - function for stop all async process in pool
// info.run() - function for execute in async pool
// [ run ] // length > 0 for array destruction detect
// // info.data?
// // info.error?
// And possible to call `info` equivalents as info.run (I think function inheritance in throttle and debounce possible)
info.run();
assert(info.proc === 1);
assert(info.pending === true);
// It's possible that pure `pool` not so interesting for use.
pool.single(async ({ cancelled }) => {
const zip = await search();
if (cancelled()) return;
return await unpack(zip);
});
// If exists active promise each query return It
// pool.throttle(150, async ({ cancelled, abortController?.., cancel }) => {
pool.throttle(150, async ({ stopped, abortController?.., stop }) => {
const zip = await search();
if (zip === 0) stop();
if (stopped()) return;
return await unpack(zip);
});
pool.debounce(150, async function * ({ cancel }) {
const zip = yield search();
if (zip === 0) cancel();
return yield unpack(zip);
});
Two kind of syntax:
// 1.
const a = ready();
const b = signal();
const c = value();
a.reset();
b.reset();
c.reset();
// or
reset(a)
reset(b)
reset(c)
const a = value(5);
const b = value(1);
const unsub = a.interceptor((new_value, current_value, stop?) => {
if (new_value == 1) {
b.set(10);
stop();
return;
}
return current_value + new_value;
});
// stoppable supported
a(1); // b.val === 10, a.val === 5
a(2); // a.val === 7
const s = signal();
const v = value();
on(s, (s_v) => v.val += s_v);
on(() => [s,v], console.log);
on.transaction(s, (s_v) => v.val += s_v); // used one transaction with changed s
// Think about
import React from "react";
import { value, useLocal, useValue } from "realar";
const Logic = () => {
const name = value("Joe");
const inputHandler = name.pre((ev: React.ChangeEvent<HTMLInputElement>) => (
ev.currentTarget.value
));
return {
name,
inputHandler
}
};
const Form = () => {
const logic = useLocal(Logic);
const name = useValue(logic.name);
return (
<>
<h1>Hello {name}!</h1>
<p>
Name:
<input
type="text"
value={name}
onChange={logic.inputHandler} />
</p>
</>
);
};
const App = () => (
<Form />
);
export default App;
effect(() => () => ( // Ugly (((
this.items_destroy && this.items_destroy()
));
// Must be pretty
unsub(() => {
this.items_destroy && this.items_destroy()
});
// ... Think about
// Or perfect one!
un(() => {
this.items_destroy && this.items_destroy()
});
const q = queue<Type?>(init?: Type[]);
// q(10); // Think about queue - is a reactive value with set of elements or is the signal for add next one... Hmm.
// q.queue // []
q.front // top
q.back // last element
// q.all // array of elements in queue
q.size
q.clear()
//
q.val
q.first: Value
q.last: Value
q.size: Value
q.active: Value
q.clear()
// Support queue for q.wrap and q.view // think about api
// val: <array_of_elements> readonly
// q(<new_element>)
Or q() - no possible, but
q.add(value: Type)
q.val: Type
q.first: Value
q.last: Value
q.size: Value
q.active: Value
q.clear()
// How I can make queue with limited size same as in my swipe impl?
q = queue();
q(values: Type[]);
q.set(values: Type[]);
q.update(...);
q.val: Type;
q.add(value: Type);
q.first: Value
q.last: Value
q.size: Value
q.active: Value
q.empty: Value
q.clear()
//
const t = q.first
t.val: Type
t.started: Value
t.start();
t.release(); // remove item from queue (or dequeue)
// or
t.val: Type
t.start: ready(false).to(true)
t.remove(); // remove item from queue // or ready(false).to(true) too
export const buttonLangPress = signal().wrap(() => {
const stop = stoppable();
if (buttonLangDisabled.val) {
stop();
}
});
// -->
export const buttonLangPress = signal().filter(() => (
!buttonLangDisabled.val
))
Think about It
const a = value(0);
const s = signal.combine(a, () => a.val + 1); // readonly
s.watch(
([a, a_plus_one]) => console.log(a, a_plus_one)
);
// And I think it's possible to make `value.combine`
// Should be detected changes in each value and if some of them changed, fire changes outside
const v = value.combine(a, () => a.val + 1); // readonly
Hi performant binding signal, value or another store, to JSX.
That property provide jsx node with binded reactive value.
return (
<p>{v.jsx}</p>
)
v.jsx
- return JSX node with value binded
return (
<p>{v.jsx.map(m => <i>{m}</i>)}</p>
)
return (
<p>{v.jsx.if(k, m => <i>{m}</i>)}</p>
<p>{v.jsx.else(m => <i>{m}</i>)}</p>
<p>{v.jsx.ifelse(m => <i>{m}</i>)}</p>
)
isolate(on());
isolate(() => {
on()
sync();
});
untrack(() => {
});
transaction(() => {
});
export const cardListChangeEffectStart = signal<Direction>();
export const cardListChangeEffectFinished = signal();
export const cardListChangeEffectInProgress = signal(false);
cardListChangeEffectInProgress.sub(cardListChangeEffectStart, () => true); // ^~~~ error
cardListChangeEffectInProgress.sub(cardListChangeEffectFinished, () => false); // ^~~~ error
class CounterLogic {
@prop value = 0;
inc = () => (this.value += 1);
constructor() {
console.log(this.value);
}
}
let n = 0;
const Counter = observe(() => {
const { inc } = useLocal(CounterLogic);
console.log("Render", n++);
return <button onClick={inc}>+</button>;
});
export const App = () => <Counter />;
The component was subscribed to value
and reacted the first time on It change, but not used inside.
export class Locale {
constructor() {
link(on(() => this.code, (code) => console.log(code)));
}
createText = (source: TextSource) => {
const text = value(source[this.code]);
on(() => this.code, (code) => {
text(source[code]);
});
}
}
Signature:
link(destructor);
Examples:
link(on(() => this.code, (code) => console.log(code)));
destruct(on(() => this.code, (code) => console.log(code)));
sub(on(() => this.code, (code) => console.log(code)));
But is it not comfortable.
isolate(on(() => this.code, (code) => console.log(code)));
For removing on
destructor from context scope.
loop(async (stop) => {
const data = await action;
if (stop.val) return;
// ...
});
loop(function* (stop) {
const data = yield action;
// ...
});
cycle((stop) => {
this.a = a.val;
});
You need to take this as a shortcut to the stoppable
mechanism.
Realar is the embedded decision for complex apps.
My dream is an extension for javascript/typescript syntax. It's will be amazing))
let ^a = 0; // creation of reactive value
let ^b = a + a;
on(a, console.log); // for reaction
a = 10
(a += 'hello').splice(0, 1); // will be reacted
// composite reaction support
on(() => a + 77, console.log);
// of course
return (
<p>{a}</p> // for jsx bindings
);
class {
^a = 10; // As usually property using
}
const a = value(0);
const new_a_with_wrap_filter = a.wrap.filter(v => v);
// or maybe
const new_a_as_filtered_source = a.flow.filter(v => v);
New naming convention implemented:
https://github.com/betula/realar/blob/master/src/index.ts#L89-L139
Add synthetic value creator function
const v = synthetic(set, get); // make new synthetic value
v.val
v.update
v.reset !! //no defined here)
Or provide possibility for creation both synthetic entities value and signal.
const s = signal.synthetic(set, get);
// or
const s = signal.from(set, get); // without reset method
// and of course
const v = value.synthetic(set, get);
// or
const v = value.from(set, get);
const r = ready.synthetic(set, get);
// or
const r = ready.from(set, get);
No have possibility to use "bind", because in classes with decorators style exists a usually "bind" decorator function that means bind context of the class method to this.
const v = value(0);
class A {
@link v = v;
}
console.log(shared(A).v); // 0
For example, the cache
alias will be
class A {
@link c = selector(() => 0);
}
shared(A).c // 0
But no possible to reassign.
The basic level of scopes for React developers is a component level scope (for example useState
, and other React hooks has that level).
Every React component instance has its own local state, which is saved every render for the component as long as the component is mounted.
In the Realar ecosystem useLocal
hook used to make components local state.
const CounterLogic = () => {
const [get, set] = box(0);
const inc = () => set(get() + 1);
return sel(() => ({
value: get(),
inc
}));
}
const Counter = () => {
const { value, inc } = useLocal(CounterLogic);
return (
<p>{value} <button onClick={inc}>+</button></p>
);
}
export const App = () => (
<>
<Counter />
<Counter />
</>
);
Or If you coding in classes style:
class CounterLogic {
@prop value = 0;
inc = () => this.value += 1
}
const Counter = () => {
const { value, inc } = useLocal(CounterLogic);
return (
<p>{value} <button onClick={inc}>+</button></p>
);
}
This feature can be useful for removing logic from the body of a component to keep that free of unnecessary code, and therefore cleaner.
selector(init_value?, (stop) => {});
selector((stop?) => {});
cycle((stop) => {});
on((stop) => {}, () => {});
// But I think pool no need to use "(stop) =>" syntax, maybe stoppable() is better for pool?
// pool.stop
pool(async () => {
const stop = pool.stop;
pool.stop();
pool.stop.throw();
});
pool.stoppable((stop) => {
return async () => {
}
});
const a = value(0);
return (
<p>{a}</p>
);
loop(async () => {
await Promise.all([signal1, signal2]);
signal3();
});
// [] And yield support in future
API
This abstraction is necessary for the implementation of serial operations. Reactions on each section of the serial operations.
const m = machine()
.step(a, () => {})
.step(b, () => 0)
.step(machine.oneOf()
.select(a, () => {})
.select(b)
.select(c), () => {})
.loop();
But I think the yield generator function is an interesting way of decision.
loop(function *() {
yield a;
yield b;
yield loop.oneOf([a, b, c]) // or loop.oneOf().select(a, () => {}).select(b, () => {})... chain function
yield loop.race([a, b, c])
yield loop.all([a, b, c])
// Promise namespace operations for suggest
});
Remove basic API functions:
New API functions:
const a = value(0);
a.val += 10;
console.log(a.val);
a(11) // change value to 11
const s = selector(() => a.val + 10);
console.log(s.val);
Remove action
New API signal
const i = signal(10);
console.log(i.val);
i(10) // call signal
const a = action();
const b = action(a, (next) => {
setTimeout(next, 1000)
});
on(b, () => console.log('to console after 1000ms'));
a();
What do you think about that syntax? It's will be interesting or not?
const a = value(0);
return (
<p>{a.jsx}</p>
)
return (
<p>{a.jsx(v => v.map((i, k) => <i key={k}>{i}</i>))}</p>
)
๐
a.jsx
a.jsx.if
a.jsx.else
a.jsx.ifelse
a.jsx.map
// or
jsx(a)
jsx.if(a)
jsx.map(a)
// etc..
For making super performant interfaces ๐
const swipeOffset = value(0);
const local_offset = value(0);
const stop = signal.stop();
const k = swipeOffset.flow.filter(() => !stop.val);
k.watch(local_offset);
Argument of type 'Value<number, number>' is not assignable to parameter of type '(value: Ensurable<number>, prev: Ensurable<number>) => void'.
Types of parameters 'data' and 'value' are incompatible.
Type 'Ensurable<number>' is not assignable to type 'number'.
Type 'void' is not assignable to type 'number'
https://github.com/betula/realar/blob/master/src/index.ts#L298-L300
function loop(body: () => Promise<any>) {
let running = 1;
const fn = async () => {
while (running) await body();
};
...
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.