Git Product home page Git Product logo

cellx's People

Contributors

arvitaly avatar nin--jin avatar oksana1988 avatar riim avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cellx's Issues

Опциональная асинхронность и транзакции

Сейчас все обновления делаются асинхронно, на следующем тике.
Добавьте пожалуйста настройку для смены такого поведения на синхронное и метод для транзакции по аналогии с mobx.transaction. Есть много причин, почему такое поведение бывает удобнее, например, отладка и обработка ошибок.

Я делаю atom overlay https://github.com/zerkalica/atmover, который является абстракцией над разными реализациями атомов и сейчас для cellx transaction реализован так:

function transact(f: () => void): void {
            f()
            cellx.Cell.forceRelease()
}

Вроде cellx.Cell.forceRelease не описан в api, следовательно может поменяться поведение.

Минимальное апи. extract BaseCell

По аналогии с fail не хватает pend. Т.к. управлять статусом через pull неудобно.

Хорошее апи должно быть простым, минимальным и расширяемым без рефакторинга. Например атомы в clojure.

В базовом классе Cell много всего, нельзя ли выделить основной класс только с минимальными апи: computable/get/set/subscribe/reap и доступом к masters, что б можно было на основе этого лепить свои isPending, getError, fail, pend, pull, put.

Не всегда этот стаф нужен. Взгляни на derivable он в 2 раза меньше cellx, но его апи позволяет навернуть всю эту мишуру с pull/put/errors/pending поверх. При том, что в нем тоже дофига лишнего: линзы, реакторы и т.д.

Базовый cellx может быть еще меньше, кил в 5 минифицированного кода.

Good way to implement auto-run?

Hi @Riim,

Could you please guide me how to implement autorun function.
It will autorun when any cell inside it changed.
when([cell1, cell2, cell3], doUpdateView)

I come up with:

function when(deps, runner) {
  const disposers = []
  deps.forEach(dep => {
    dep.subscribe(runner)
    disposers.push(() => dep.unsubscribe(runner))
  })

  return () => disposers.forEach(f => f())
}

I wonder if there is any better way?

isPending, getError упаковать в один метод

Предлагаю ввести понятие метаданных атома, которые тоже являются атомом, но просачиваются сквозь computables, комбинируясь по нижеприведенной схеме.
Т.е. упаковать isPending, getError в один метод getStatus(): FetchStatus.
По признаку того, что это все метаданные атома. Статусы могут со временем дополняться, состояние метаданных атома не ограничивается pending/error, рефакторить будет проще.

// @flow

export type FetchStatusType = 'error' | 'pending' | 'complete'

export default class FetchStatus {
    complete: boolean
    pending: boolean
    error: ?Error

    constructor(
        type?: FetchStatusType,
        error?: ?Error
    ) {
        this.complete = type === 'complete'
        this.pending = !type || type === 'pending'
        this.error = error || null
    }

    static merge(statuses: FetchStatus[]): FetchStatus {
        const newStatus = new FetchStatus('complete')
        for (let i = 0, l = statuses.length; i < l; i++) {
            const status = statuses[i]
            if (status.pending) {
                newStatus.pending = true
                newStatus.complete = false
            }
            if (status.error) {
                newStatus.error = status.error
                newStatus.complete = false
                newStatus.pending = false
                break
            }
        }

        return newStatus
    }
}

why a varargs/overloaded API?

hey @Riim,

why this

user.fullName('subscribe', function() {
    console.log('fullName: ' + this.fullName());
});

instead of this?

user.fullName.subscribe(function() {
    console.log('fullName: ' + this.fullName());
})

there could be better benefits for API discoverability, minification and performance by using separate instance methods instead of overloading the getter/setter functionality and relying on arg count.

what do you think?

pull в ICellOptions

сейчас конструктор Cell такой:

constructor(value?: T, opts?: ICellOptions<T>);
constructor(pull: ICellPull, opts?: ICellOptions<T>);

На мой взгляд правильнее добавить pull в ICellOptions, тем более что там уже есть put, reap и т.д.

interface ICellOptions<T> {
  pull: (push: (value: any) => void, fail: (err: any) => void, oldValue: any): any;
 ...

Тогда и неаккуратность с oldValue === undefined при первом выполнении pull разрешится:

cellx(1, {
  pull(push, fail, oldValue) {

  }
})

Да и в принципе oldValue в pull не нужен вроде в таком случае.

Есть еще один аспект, например когда pull функция генерируется кем-то, можно не генерить функции, а экземпляр класса создать, ресурсов меньше.

Зачем `let reactions = this._reactions;`

Привет.
А зачем перед какой-либо операцией над this._reactions ссылка на массив сначала копируется в отдельную переменную и уже над ней производится операция, например:

let reactions = this._reactions;

?

После fail executon всея ячейки дерева state ACTUAL

Привет.

Пример простого дерева:

const a = new Cell(() => b.get());
const b = new Cell(() => c.get());
const c = new Cell(() => {
	throw new Error('123');
});
a.onChange(()=>{});
expect(a._state).eq(CellState.ACTUAL)
expect(a._error).exist;
expect(b._state).eq(CellState.ACTUAL)
expect(b._error).exist;
expect(c._state).eq(CellState.ACTUAL)
expect(c._error).exist;

Каждая из ячеек содержит ошибку и при этом ее состояние актуально.
Вроде странно, но ок если принять, что error наряду с value является частью состояния ячейки.
Изменил состояние error - сделал статус актуальным(если соблюдаются условия).

Вот пример по-интереснее:

const truthy = new Cell(true);
const a = new Cell(() => b.get());
const b = new Cell(() => {
	const prefix = truthy.get() ? 'truthy' : r.get();
	if (!truthy.get())
		throw new Error('not truthy');
	return prefix + c.get();
});
const c = new Cell(() => d.get());
const d = new Cell(7);
const r = new Cell(() => s.get());
const s = new Cell('hello');

a.onChange(()=>{});
truthy.set(false);
Cell.release();
expect(a._state).eq(CellState.ACTUAL)
expect(a._error).exist;
expect(b._state).eq(CellState.ACTUAL)
expect(b._error).exist;

после truthy.set(false);
от b строится ветка b -> r -> s,
но при этом из-за ошибки отваливается ветка b -> c -> d.
Тело функции выполнено не до конца, появились новые зависимости. Старые зависимости отвалились из-за экстренного выхода при выполнении функции. И в общем случае неизвестно сколько новых веток не создалось.
Есть ощущение, что в таком кейсе ставить b и всем ее реакциям статус ACTUAL не совсем верно.

Это я все к тому, что может быть есть смысл всем ячейкам, где возникла ошибка, ставить DIRTY?

isPending не переключается в true при выполнении put

cellx.configure({asynchronous: false})

// let a = new cellx.Cell(1)
let b = new cellx.Cell(1, {put: (v, push) => {
  setTimeout(() => {
    push(v)
  }, 2)
}})

const c = new cellx.Cell(() => {
  return {
    v: b.get(),
    p: b.isPending()
  }
})

c.subscribe((err, {value}) => {
  console.log('b.subscribe, err: ' + (err ? err.message : 'null') + ', val: ' + value.v+ ', isPending = '+value.p)
})

const v = c.get()

console.log(v)

b.set(2)
console.log(b.isPending())

/*
[object Object] {
  p: false,
  v: 1
}
false
"b.subscribe, err: null, val: 2, isPending = false"
*/

http://jsbin.com/cuxahajifo/1/edit?js,console

Как сделать подписку с учётом будущих изменений?

Приветствую. Спасибо за интересный проект.

Сразу скажу: я — тупой. Реактивное программирование и в целом программирование для меня скорее вынужденное занятие, но тем не менее РП меня заинтересовало своей гибкостью.

Захотел сделать асинхронный конвейер событий где можно жить с коллбеками в коллбеках, но столкнулся с проблемой.
Вот упрощённый пример:

var queueHack = function(level) {
	var count = 0;
	for (var key in level) {
		count++;
		if (level[key]() !== true && count !== 1) {
			return false;
		}
	}
	return true;
};
var test = cellx(function() {
	return queueHack(this);
});
test["a"] = cellx();
test["b"] = cellx(function() {
	return queueHack(test["b"]);
});
test["b"]["a"] = cellx();
test["b"]["b"] = cellx();
test["c"] = cellx();
test["a"](true);
test["b"]["a"](true);
test["b"]["b"](false);
test["c"](true);
test();
// false

Тут всё хорошо, но стоит написать так:

var queueHack = function(level) {
	var count = 0;
	for (var key in level) {
		count++;
		if (level[key]() !== true && count !== 1) {
			return false;
		}
	}
	return true;
};
var test = cellx(function() {
	return queueHack(this);
});
test("subscribe", function(err, evt) {
	if (err) {
		console.log("error", err); return;
	}
	console.log("evt", evt);
})
test["a"] = cellx();
test["b"] = cellx(function() {
	return queueHack(test["b"]);
});
test["b"]["a"] = cellx();
test["b"]["b"] = cellx();
test["c"] = cellx();
test["a"](true);
test["b"]["a"](true);
test["b"]["b"](false);
test["c"](true);
test();
// true

Как видно после подписки последующие изменения уже не учитываются.
Так и должно быть?

Проблема в том что в моей задаче сначала декларативно через функцию создаётся родительная ячейка и сразу же на неё делается подписка, а уже потом в неё могут дополниться дополнительные ячейки.
В общем я делаю очередь с иерархией подписок:
1.
1.1.
1.1.1
1.1.2
1.1.3
1.2.
1.2.1
1.2.2
1.2.3
Где каждый подуровень это каллбек (XHR внутри которого обращение к БД).
Точнее говоря тут в одном цикле вызывается каллбек, потом внутри этого каллбека ещё один цикл который вызывает ещё один каллбек и мне надо когда все они закончат работу вызвать событие.
Вот попытался клетки под это адаптировать, но не вышло.
Я скорее всего делаю что-то не так, но моих навыков не хватает понять как правильнее.
Поэтому сейчас пытаюсь сделать это более классической очередью событий, но подумал и посчитал нужным что стоит написать про проблему с подпиской.

Как сделать ячейку read-only?

Не самое важное, но сейчас ячейка из любого места доступна для изменения. Или предполагается, что нужно прописывать функцию с throw в свойство put второго параметра при создании?

Почему dep._addReaction после pull делает ячейку DIRTY?

Привет.

Вопрос конкретно по этой строчке:
https://github.com/Riim/cellx/blob/master/src/Cell.ts#L615

Почему передается actual false?

Например:

const direct = new Cell(true);
const a = new Cell(() => direct.get() ? 3 : b.get());
const b = new Cell(() => c.get());
const c = new Cell(() => 9);

a.onChange(()=>{});
direct.set(false);
Cell.release();
expect(b._state).eq(CellState.DIRTY);

После direct.set(false):

a._pull() -> b._pull() -> c._pull()

Вроде бы по логике состояние b должно быть гарантированно ACTUAL.
Можешь объяснить как так?

Guidance requested

Thanks for the awesome library. I'm looking for some architectural guidance

I'm looking at using cellx as the basis for a calculation engine, much like excel.

I'd like to have a bunch of cellx variables that represent cells on a spreadsheet, i.e. columns and rows

Some of these have formulas that get computed based on changes in other cells.

For example, say I have A1 = cellx(), A2 =cellx(), etc, etc and I want A3 to be the sum of A1+A2

When A1 or A2 changes, I'd like A3 to be updated automatically.

I'd like to have a matrix of rows and columns, like excel that are reactive to changes in the cells.

Any help would be appreciated ... thanks.

Descriptor doesn't have an initializer method

Probably you're using Babel to transpile your code, and that's why you're using var value = descr.initializer();, but it won't work with other transpilers (such as TypeScript), nor with vanilla ES6.

Couldn't you be using descr.value directly?

Лишний рендер для isPending

В примере ниже лишний: "c.subscribe: isPending: true, err: null, val: 5", т.к. данные сфетчились и записались, а потом еще отдельно isPending в false переключили

let a = new cellx.Cell((push, fail, old) => {
  setTimeout(() => {
    push(2)
  }, 100)
  return old || 1
})

let b = new cellx.Cell((push, fail, old) => {
  setTimeout(() => {
    push(3)
  }, 500)
  return old || 2
})


cellx.configure({asynchronous: false})
const c = new cellx.Cell(() => a.get() + b.get())
const d = new cellx.Cell(() => {
  return {
    v: c.get(),
    isPending: c.isPending(),
    error: c.getError()
  }
})

d.subscribe((err, {value}) => {
  console.log('c.subscribe: isPending: '+value.isPending + ', err: ' + (err ? err.message : 'null') + ', val: ' + value.v)
})

const v = d.get()
console.log('c.get:', v)
/*
"c.get:"
[object Object] {
  error: null,
  isPending: true,
  v: 3
}
"c.subscribe: isPending: true, err: null, val: 4"
"c.subscribe: isPending: true, err: null, val: 5"
"c.subscribe: isPending: false, err: null, val: 5"
*/

http://jsbin.com/padehafano/edit?js,console

Cancel для pull обновлений

Сейчас загрузка данных по по факту запроса данных из ячейки работает так:

var foo = cellx(function(push, fail, oldValue) {
    request.get('http://...').then(function(res) {
        if (res.ok) {
            push(res.value);
        } else {
            fail(res.error);
        }
    });

    return oldValue || 0;
})

Но потребители, в частном случае компоненты, не только запрашивают данные (componentDidMount), но еще могут обрывать загрузку (componentWillUnmount).

Нельзя ли второе свойство тоже что б умел cellx?

const a = cellx(function(push, fail, oldValue, onCancel) {
   const promise = request.get('http://...') 
   promise.then(function(res) {
        if (res.ok) {
            push(res.value);
        } else {
            fail(res.error);
        }
    });
    onCancel(() => promise.cancel())

    return oldValue || 0;
})

const b = cellx(() => a() + 1)
b.get() - в a пошла загрузка
b.release() - Загрузка  в a отменилась

Сломаны removeChangeListener и unsubscribe

Для примера

test = cellx();
test("subscribe", function(err, evt) {
	console.log("evt", evt);
})
test("unsubscribe", 0)
test("unsubscribe", undefined) // Uncaught TypeError: Cannot read property 'Symbol(wrappers)' of undefined
test("unsubscribe", function(){})

Всё варианты не работают.
Аналогично с removeChangeListener.
Что удивительно

test("dispose", 0)

удалило подписку, а сама ячейка осталась жива и работоспособна.

dispose throws unexpectedly

  const  {cellx} = require('cellx');

  var A = cellx(5);

  var foo1 = cellx(() => 2 + A());
  var foo2 = cellx(() => 1 + A());

  foo1('subscribe', () => { console.log(foo1()); });
  foo2('subscribe', () => { console.log(foo2()); });

  A('dispose', 0);

The last line throws:
TypeError: Cannot read property 'reap' of undefined

Only happens if more than one dependent cell, each with an extra subscriber

Ожидание корректного значения

Привет.

В случае rxjs значение из Observable может не выйти до тех пор, пока не сработает условие, например:

ob$.pipe(
  ...
  filter(x => !!x),
)

Весь код, использующий ob$, просто ждет значения.
Как сделать тоже самое на cellx?

Error rethrowed only once

a = cellx(0)
b = cellx(()=>{ console.xxx() })
b() // cellx.umd.js:45 TypeError: console.xxx is not a function
b() // undefined

Второй обратившийся к ячейке вместо актуального значения или исключения получает undefined и скорее всего его логика тоже сломается и бросит уже другое исключение. И так каскадом.

Accessing any cell causes all change events to be fired?

a = cellx(1);
b = cellx(10);
c = cellx(() => a() + b());

c('subscribe', () => { console.log("C is", c()); })

a(2); a(3); // "C is 13"
a(4); b(); a(5); // "C is 14" "C is 15"

In this example, just getting b() apparently causes c to be recalculated and change events to fire. That would mean you have to be very careful not to read any cells if you want multiple changes to result in one change event.

Не происходит асинхронная подписка на ячейку?

Привет, или я чего-то не понимаю в логике или это ошибка? Пытаюсь вычислить значение x через a асинхронно, значение вычисляет в первый раз, однако после этого для x addChangeListener не вызывается при изменении a.

https://gist.github.com/arvitaly/b3335f415d14c0bdcb9fa036035ad424

Синтаксис вызова методов

Кстати, а какова причина такого синтаксиса вызова метода? Почему не через точку?

var targetCell = cellx();
targetCell.subscribe(()=>{

})

Тогда и 0 не придется писать для non-arguments методов?

Одно вычисление а не два

Привет! Ты не мог ли немного пояснить за счет чего достигается след. фича: есть три переменных a,b,c, где c = a() + b(); Если мы меняем a и b, то c вычисляется единожды.

Возможность извлечь cell из метаданных объекта

Есть ли способ извлечь ячейку из метаданных объекта, если таковой передается как значение?
Например,

const o = cellx({a: 1})
const s = Symbol('cellx')

o[s].set({a: 2})

Просто делая atmover, я стремлюсь максимально тонкую прослойку делать с cellx, сейчас ко всем значениям-объектам добавляю свойство-символ с ссылкой на cellx, может оно уже где-то добавляется в cellx?

Async cell

Привет.

Почитал в редми и не смог понять как сделать асинхронную ячейку( Подскажи пожалуйста.
У меня есть такой Api:

class Api {
  w = new Cell(() => this.triggerW.get());
  triggerW = new Cell(7);

  p = new Cell(() => this.triggerP.get());
  triggerP = new Cell(5);

  z = new Cell(() => this.triggerZ.get());
  triggerZ = new Cell(3);

  async request(): Promise<string> {
    const wResult = `w[${this.w.get()}]`;
    const pResult = await new Promise(resolve => {
      const pValue = this.p.get();
      setTimeout(() => resolve(`p[${pValue}]`), 50); // e.g. request to the server
    });
    return `${wResult} ${pResult} z[${this.z.get()}]`;
  }
}

Надо сделать что-то вроде такого:

const api = new Api();
const rootCell = new Cell(() => asyncCell.get());
const asyncCell = new AsyncCell(() => api.request());
rootCell.onChange(event => {
  console.log('пришло из асинхронной ячейки:', event.data);
});
api.triggerW.set('hello world');

Функция в качестве значения атома, а не pull

Нет ли способа использовать функцию без объектов-оберток в качестве значения атома cellx(fn)? Сейчас он пытается функцию вычислять.

Я хочу сделать hot-reloading, а в нем, когда мы меняем код, то меняется экспортируемый модулем класс, если бы класс был реактивным значением, то можно было заново его переинициализировать со старыми значениями.

import cellx from 'cellx'

interface IA {}

class A implements IA {}
class B implements IA {}

const classA = cellx(A)
const argsA = cellx([])

const a = cellx(() => new classA.get()(...argsA.get()))

a.get() instanceof A

classA.set(B)

a.get() instanceof B

Bug с isPending

Тут никогда не сработает ресорв b:

let a = new cellx.Cell((push, fail, oldValue) => {
  setTimeout(() => {
     push(1)
  }, 500)
  return oldValue || 0
});


let b = new cellx.Cell((push, fail, oldValue) => {
  setTimeout(() => {
     push(2)
  },700)
  return oldValue || 0
});

var c = new cellx.Cell(() => a.get() + b.get())
var d = new cellx.Cell(() => ({
  isPending: c.isPending(),
  c: c.get()
}))

d.subscribe((err, {value}) => {
  if (err) {
    console.error(err)
  }
  console.log('d.c = ' + value.c + ', d.isPending = ' + value.isPending)
})

const value = d.get()
console.log('start, d.c = ' + value.c + ', d.isPending = ' + value.isPending)


// "start, d.c = 0, d.isPending = false"
// "d.c = 1, d.isPending = true"

"d.c = 3, d.isPending = false" никогда не покажется.

http://jsbin.com/kuzuquzupe/edit?js,console

_deactivate() не чистит this._dependencies

Привет.

после очистки всех зависимостей от реакции:
https://github.com/Riim/cellx/blob/master/src/Cell.ts#L482

this._dependencies же не чистится, соответственно, держит ссылки на зависимости, которые уже ничего не знают об этой реакции.

Если это норм, скажи зачем их хранить, ведь даже state стал DIRTY?

Как организовать показ загрузчика и ошибки?

Как быть, если есть некий UI, который зависит от нескольких ячеек, которые заполняются асинхронно, и их первое значение не имеет смысла?
Создать отдельную ячейку isLoading и isError, в которых проверять появились ли значения во всех нужных ячейках?

А нужен ли put?

Начнем с того, что при сохранение атома выглядит странно: set(some) вызывает fetch. Для синхронизации это может и годится. Сохранению обычно

  1. предшествуют какие-то изменения в интерфейсе, индикатор, ошибки валидации и т.д. А put получается навязывает подход, когда мы это все делаем не прямо, а опосредованно после вызова set.
  2. Сохранение чего-то вообще не подразумевает наличие модели, POST запрос, сохранивший todo может ничего не вернуть кроме 200 кода успеха, получается делаем атом, только что б вытянуть ошибки и isPending. Атом становится своего рода контроллером, сервисом, а изначальная его идея - только реактивное распространение данных.
  3. Модель на сохранение может не соответствовать модели на загрузку, например могут быть какие-то метаданные только для сервера, логирование и т.д.
  4. Не всегда реакция на fetch - изменение стейта, это может быть переход на новый location, посылка еще одно запроса куда-либо и т.д.

Есть форма добавления Todo.
Данные формы и ее ошибки - 2 разных атома.
При сохранении данных надо проверить данные, обновить атом с ошибками, если ошибок нет - выполнить сохранение на сервер. Причем кроме todo, надо сохранить еще кучу данных, не имеющих отношения к нему, ну апи такое. После этого надо еще todos обновить на клиенте.

type Todo = {title?: string}

const todos = new cellx.Cell((push, fail, oldValue) => {
 fetch('/todos', {method: 'GET'})
  .then((todos) => push(todos))
  .catch(fail)

  return oldValue || []
})

const todoErrors = new cellx.Cell({})
const todoOnServer = new cellx.Cell({}, {
  put(t: {todos: Todo[], todo: Todo, counter: number, canAddEmpty: boolean}, push, fail) {
      fetch('/todo', {method: 'POST', body: JSON.stringify(t)})
        .then(() => {
             push()
             t.todos.set(t.todos.get().push(t.todo))
        })
        .catch(fail)
  }
})
const editableTodo = new cellx.Cell({})

function editTitle(title: string) {
  editableTodo.set({title})
}
let counter = 0
function saveTodo(canAddEmpty: boolean) {
  conts o = {}
  const todo = editableTodo.get()
  if (!canAddEmpty && !todo.title) {
    o.isErrors = true
    o.title = 'Title is empty'
  }
  todoErrors.set(o)
  if (!o.isError) {
     todoOnServer.set({todo, todos, counter: counter++, canAddEmpty})
  }
}
  1. Экшен saveTodo, который выполняет сохранение, формирует данные для передачи на сервер.
  2. Эти данные складываются из аргументов, с которым вызвали экшен (canAddEmpty) и контекста (editableTodo, counter, todos).
  3. А что если нет синглотнов и метод put ничего не знает о контексте. Получается, надо формировать объект всего-всего, что необходимо для передачи на сервер и, что важно, обновления стейта (нам же todos надо обновить)

В примере выше, видно, что todoOnServer - это уже не todo, а некая сущность с с todo, counter, canAddEmpty и на клиенте эта сущность нужна только для показа ошибок от сервера. При чтении у нас вообще отдельная коллекция из другого апи.

Вместо прямого fetch в saveTodo, мы упаковываем todo и canAddEmpty в один объект, сетим с ним атом, а атом уже делает fetch только ради состояния ошибок.

По мне, это и есть основная дырявость абстракции put: не очевидность сохранения, сценарий получается размазанным по saveTodo и put.

С прямым fetch в общем-то простой saveTodo получается, без лишних абстракций.

function saveTodo(canAddEmpty: boolean) {
  // ...
  if (!o.isError) {
      const p = fetch('/todo', {method: 'POST', body: JSON.stringify({todo: editableTodo.get(), counter: counter++ cannAddEmpty})})
        .then(() => {
             todos.set(todos.get().push(t.todo))
        })

        todoState.setPromise(p)
  }
}

"effects" in cellx

In Solid, it is:

createEffect(() => {
  console.log(firstName() + ' ' + lastName())
})

I suppose in cellx the same is a cellx but just don't use the value:

cellx(() => {
  console.log(firstName() + ' ' + lastName())
})

So "effects" and "signals" are under a single API. Personally I prefer dependency-tracking effect patterns of subcription or event patterns because the automatic dependency tracking is what leads to concise and clean code; otherwise having to manage subscriptions/events gets more verbose and less flexible, similar to always using on in Solid. (Article coming soon on how dependency tracking simplifies code compared to repeated promises, subscriptions, and events).

Не работают транзакции внутри put

С optimistic updates бывает на fail одного атома, надо откатить 10 других, как сделать это в транзакции?

let a = new cellx.Cell(1)

let b = new cellx.Cell(1, {
  put(val, push, fail) {
     setTimeout(() => {
        cellx.transact(() => {
            a.set(3)
            fail(new Error('some error'))
        })
     }, 0)
  }
})

cellx.configure({asynchronous: false})
const c = new cellx.Cell(() => a.get() + b.get())
c.subscribe((err, {value}) => {
  console.log('c.subscribe: ' + (err ? err.message : '') + ', val: ' + value)
})
console.log('c.get:'+ c.get())
b.set(2)

/*
"c.get:2"
"Error: some error
    at cellx.transact (vudoyofase.js:8:18)
    at Function.transaction (https://npmcdn.com/[email protected]/dist/cellx.min.js:1:17210)
    at setTimeout (vudoyofase.js:6:15)"
"c.subscribe: some error, val: undefined"
"c.subscribe: , val: 4"
*/

http://jsbin.com/wajoxapuvo/edit?js,console

Думаю, аналогично для pull

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.