Esquizofrenia fractal. ¿Qué pasa?







Según algunas fuentes, allá por el IV a. C., Aristóteles hizo una pregunta sencilla: ¿Qué pasó antes? ¿Huevo o gallina? Él mismo finalmente llegó a la conclusión de que ambos aparecieron al mismo tiempo, ¡esto es un giro! ¿No lo es?







, , - . , , , — , , — , (, ..).







. . . , , .







. — , . — . ?







What's up guys?



- - .







npm i whatsup
      
      





— . . api. .







, react



+ mobx



, , 5kb gzip



.







, — , , . . — , , , , .







Cause & Conse



. . computed



observable



, , , , .







. , ( , — ).







const name = conse('John')

//     - What`s up name?
whatsUp(name, (v) => console.log(v))
//   :
//> "John"

name.set('Barry')
//> "Barry"
      
      





CodeSandbox







, ? conse



, whatsUp



— "" . .set(...)



— — .







Conse



Cause



. , yield*



— "" , , , yield*



return



( yield



, )







const name = conse('John')

const user = cause(function* () {
    return {
        name: yield* name,
        //    ^^^^^^   name
        //               
    }
})

//     - What`s up user? :)
whatsUp(user, (v) => console.log(v))
//   :
//> {name: "John"}

name.set('Barry')
//> {name: "Barry"}
      
      





CodeSandbox







yield* name



user



name



, , — name



user



— — .







?



, - "". . , user



revision



, .







revision



, user



.







const name = conse('John')

let revision = 0

const user = cause(function* () {
    return {
        name: yield* name,
        revision: revision++,
    }
})

whatsUp(user, (v) => console.log(v))
//> {name: "John", revision: 0}

name.set('Barry')
//> {name: "Barry", revision: 1}
      
      





CodeSandbox







- , — revision



. — , ( ) yield



return



, , .







const name = conse('John')

const user = cause(function* () {
    let revision = 0

    while (true) {
        yield {
            name: yield* name,
            revision: revision++,
        }
    }
})

whatsUp(user, (v) => console.log(v))
//> {name: "John", revision: 0}

name.set('Barry')
//> {name: "Barry", revision: 1}
      
      





CodeSandbox







? , , . revision



, , . revision



, — .









cause



conse



— . , .







import { Cause, Conse, whatsUp } from 'whatsup'

type UserData = { name: string }

class Name extends Conse<string> {}

class User extends Cause<UserData> {
    readonly name: Name

    constructor(name: string) {
        super()
        this.name = new Name(name)
    }

    *whatsUp() {
        while (true) {
            yield {
                name: yield* this.name,
            }
        }
    }
}

const user = new User('John')

whatsUp(user, (v) => console.log(v))
//> {name: "John"}

user.name.set('Barry')
//> {name: "Barry"}
      
      





CodeSandbox







whatsUp



, .









whatsUp



. , update



— .







. , , . , try {} finally {}



.







-, 1 , setTimeout



, , clearTimeout



.







const timer = cause(function* (ctx: Context) {
    let timeoutId: number
    let i = 0

    try {
        while (true) {
            timeoutId = setTimeout(() => ctx.update(), 1000)
            //      1 

            yield i++
            //      
            //   
        }
    } finally {
        clearTimeout(timeoutId)
        //  
        console.log('Timer disposed')
    }
})

const dispose = whatsUp(timer, (v) => console.log(v))
//> 0
//> 1
//> 2
dispose()
//> 'Timer disposed'
      
      





CodeSandbox









, . .







const increment = mutator((i = -1) => i + 1)

const timer = cause(function* (ctx: Context) {
    // ...
    while (true) {
        // ...
        //    
        yield increment
    }
    // ...
})
      
      





CodeSandbox







— , . , . , undefined



, i



-1



, 0



. .. increment



i



.







. , , ===



. — . , , , . - , , , .







class EqualArr<T> extends Mutator<T[]> {
    constructor(readonly next: T[]) {}

    mutate(prev?: T[]) {
        const { next } = this

        if (
            prev && 
            prev.length === next.length && 
            prev.every((item, i) => item === next[i])
        ) {
            /*
              ,    , 
              , , 
                  
            */
            return prev
        }

        return next
    }
}

const some = cause(function* () {
    while (true) {
        yield new EqualArr([
            /*...*/
        ])
    }
})
      
      





.. , shallowEqual



, , , . , .







cause



conse



mutator



— . , Mutator



, mutate



.







— dom-. — body



, .







class Div extends Mutator<HTMLDivElement> {
    constructor(readonly text: string) {
        super()
    }

    mutate(node = document.createElement('div')) {
        node.textContent = this.text
        return node
    }
}

const name = conse('John')

const nameElement = cause(function* () {
    while (true) {
        yield new Div(yield* name)
    }
})

whatsUp(nameElement, (div) => document.body.append(div))
/*
<body>
    <div>John</div>
</body>
*/
name.set('Barry')
/*
<body>
    <div>Barry</div>
</body>
*/
      
      





CodeSandbox









— WhatsUp — , observable



, computed



, reaction



. action



, . , , .









, :) , . , , , . parent-child



— , . , ! , , .







import { Fractal, Conse, Event, Context } from 'whatsup'
import { render } from '@whatsup/jsx'

class Theme extends Conse<string> {}

class ChangeThemeEvent extends Event {
    constructor(readonly name: string) {
        super()
    }
}

class App extends Fractal<JSX.Element> {
    readonly theme = new Theme('light');
    readonly settings = new Settings()

    *whatsUp(ctx: Context) {
        //   this.theme    
        // .. " "   
        ctx.share(this.theme)

        //    ChangeThemeEvent,  
        //        
        ctx.on(ChangeThemeEvent, (e) => this.theme.set(e.name))

        while (true) {
            yield (<div>{yield* this.settings}</div>)
        }
    }
}

class Settings extends Fractal<JSX.Element> {
    *whatsUp(ctx: Context) {
        //   Theme,  -   
        const theme = ctx.get(Theme)
        //   ,  ctx.dispath
        const change = (name: string) => 
            ctx.dispath(new ChangeThemeEvent(name))

        while (true) {
            yield (
                <div>
                    <h1>Current</h1>
                    <span>{yield* theme}</span>
                    <h1>Choose</h1>
                    <button onClick={() => change('light')}>light</button>
                    <button onClick={() => change('dark')}>dark</button>
                </div>
            )
        }
    }
}

const app = new App()

render(app)
      
      





CodeSandbox







ctx.share



, - , ctx.get



.







ctx.on



, ctx.dispatch



. ctx.off



, , .







, jsx- babel- jsx-. ? — . , dom-, html-. dom ( react, ) . , . — , , dom Settings



( yield* theme



— ).







, <body>



. render



, , — .







, , .









. , , try {} catch {}



. , , .







import { conse, Fractal } from 'whatsup'
import { render } from '@whatsup/jsx'

class CounterMoreThan10Error extends Error {}

class App extends Fractal<JSX.Element> {
    *whatsUp() {
        const clicker = new Clicker()
        const reset = () => clicker.reset()

        while (true) {
            try {
                yield (<div>{yield* clicker}</div>)
            } catch (e) {
                //  ,  "" - ,
                //         
                //   -   
                if (e instanceof CounterMoreThan10Error) {
                    yield (
                        <div>
                            <div>Counter more than 10, need reset</div>
                            <button onClick={reset}>Reset</button>
                        </div>
                    )
                } else {
                    throw e
                }
            }
        }
    }
}

class Clicker extends Fractal<JSX.Element> {
    readonly count = conse(0)

    reset() {
        this.count.set(0)
    }

    increment() {
        const value = this.count.get() + 1
        this.count.set(value)
    }

    *whatsUp() {
        while (true) {
            const count = yield* this.count

            if (count > 10) {
                throw new CounterMoreThan10Error()
            }

            yield (
                <div>
                    <div>Count: {count}</div>
                    <button onClick={() => this.increment()}>increment</button>
                </div>
            )
        }
    }
}

const app = new App()

render(app)
      
      





CodeSandbox









, , , :







  • yield*



  • yield



  • return



  • throw







— , whatsup



js-framework-benchmark. - , — , : , , , , , . . , whatsup



, inferno



, preact



, vue



, react



angular















, "" , . , , " " — .







-





3 kb gzip. — whatsup



. , 5-.







Glitch free



, . "". , ( Glitches).









JustDont :







. , , , . , , 20. maximum call stack size exceeded.

. mobx whatsup ( ). : "", . a



, b



, c



, d



. a2 = b1



, b2 = a1-c1



, c2 = b1+d1



, d2 = c1



. "" . , "".







— Chrome 88.0.4324.104 (64-) mobx



1653 , Maximum call stack size exceeded



. — .







Whatsup



5, 10 100 000 — out of memory



. , . layersCount



.







cellx



(Riim — ).











, . , yield delegate(otherStream)



.









defer



, . , , .









@whatsup/route



route



redirect



. , , react-router



. , ([0-9]+)



. , .







CLI



André Lins. whatsup



- .







npm i -g @whatsup/cli
# then
whatsup project
      
      







WhatsUp



- react



-. @whatsup/react, .









Todos — TodoMVC







Loadable — , ctx.defer



, , , ,







Antistress — , , . , , . — , — , — . ,







Sierpinski — ,









, whatsup



— , — … - .







Gracias por su tiempo, espero haberlo quitado por alguna razón. Estaría encantado de recibir sus comentarios, críticas constructivas y ayuda en el desarrollo del proyecto. Pero, ¿qué hay? Incluso un asterisco en github ya es una recompensa.







Además, quiero expresar mi agradecimiento a todos los que me apoyaron, escribieron en forma personal, por correo electrónico, en vk, telegrama. No esperaba tal reacción luego de la publicación del primer artículo, fue una grata sorpresa para mí y un aliciente adicional para el desarrollo del proyecto. ¡Gracias!









Saludos cordiales, Denis Ch.







"La mayor parte de mi trabajo es la agonía del nacimiento de una nueva disciplina científica" Benoit Mandelbrot








All Articles