tinkoff / stapp Goto Github PK
View Code? Open in Web Editor NEWModular state and side-effects management for microfrontends
Home Page: https://stapp.js.org/
License: Apache License 2.0
Modular state and side-effects management for microfrontends
Home Page: https://stapp.js.org/
License: Apache License 2.0
Should investigate
type StappState<T extends Stapp<any, any>> = T extends Stapp<infer State, any> ? State : any>
// later
const app = createApp(...)
type State = StappState<typeof app>
Same for application api
There are some problems with SSR right now
When waitFor was added in any module, whenReady promise waits this event and never resolves after it
It'll be great to have some kind of timeout settings for waitFor to resolve SSR if some events was never dispatched by apps. For example
waitFor[ { event: someEventCreator, timeout: 1000 } ]
In current version whenReady resolves with full initial states of any modules. It'll be great for page size to exclude any empty reducers from final store.
For example this
app: { loaders: {}, someUsefulReducer: [1, 2, 3] }
turns into this
app: { someUsefulReducer: [1, 2, 3] }
Currently ready Promise resolves with a full app state.
Loader module doesn't use name from its constants, so the app key isn't included in module name. It results in error when another module has loader as dependencies.
Anti-clearFields
event.
Problem and motivation:
Currently, each epic is wrapped into a special wrapper, which adds a meta field to every event coming from that epic. That is useful when developing, but this mechanism doesn't cover other event sources like thunks, api methods, etc.
Solution:
Every event coming from a module should be passed through some ]custom dispatch. This would solve the problem at its core.
Problem and motivation:
Stapp comprises state-management tools, modules, React utilities, etc. At some point, this will become hard to maintain.
Solution:
Switch to the monorepo structure.
revalidate
event is exported to an application API, but is not exported directly
Problem
Currently, if the user wants to use RxJS or any other reactive library except light-observable, he needs to use from
and its analogs in every epic. stapp-rxjs
package solves this problem partially but is not convinient.
Solution
Libraries like redux-observable and recompose try to solve this problem by exposing a function (e.g.: setObservableConfig
in recompose) that sets all needed transformations globally.
Todo:
true
, force stapp to ignore global config and provide that module the native observable.stapp-rxjs
e.g. selectEvents([event1, event2]).epic({ ... })
type WaitFor = Array<AnyEventCreator | string | {
event: AnyEventCreator | string
timeout: number
+ condition: () => boolean
}>
Binded Consumer should use create-consumer package
Context-based Provider should provide value, not components
stapp-formbase
reducers should clear all values, related to passed fields.
e.g. isLoadingSelector, which will fail if there is no loading module
Currently, main typings file doesn't export any core interfaces like Module or Stapp.
Consumer will accept only one mapping function:
type MapState = <State, Api, Result>(state: State, api: Api) => Result
Result
type could be anything, not only object, because it won't be merged with API anymore. Instead children will receive Result and Api as separate arguments:
<Consumer
mapState={state => state.primitiveValue}
render={(state, api) => <div />}
/>
Problem and motivation:
Stapp is too dependent on RxJS. This makes stapp less useful for people, who use most or other reactive libraries or don't use reactive programming at all. Term epic
might be confusing.
Solution:
epic
field. Alias name is discussable, yet I propose something like process
or processors
;combineEpic
. process
field should accept an array of processors.Problem
Currently, every synchronous event dispatched from the module immediately after connecting it's epic to the store has no chance to be caught in modules following after.
Example:
const m1 = {
epic: () => of(someEvent('Hello'!))
}
const m2 => {
epic: someEvent.epic(ev$ => ev$.pipe(
tap(ev => console.log(ev.payload)
)
}
const app1 = createApp({
modules: [m1, m2] // Notice the order
})
// => nothing happens =(
const app2 = createApp({
modules: [m2, m1] // Notice the order
})
// => Hello!
This happens because of a synchronous nature of observables.
Solution
Queue all events coming from the root epic during initialization and dispatch them after that.
TBD
Consumer rerenders if map
prop is changed. It should create a new state instead and rerender only if it differs from the current state.
Problem
Effect has start, success, fail events, but for handling case when effect was finished and the result of it doesn't metter, we have to use something like this
event$ => selectArray([effect.fail, effect.success], event$)
Solution
Add finally or complete event to handle such case.
So that it can be copy-pasted.
TBD
If we try follow to link from
https://github.com/TinkoffCreditSystems/stapp/tree/master/examples/form-async-validation
Problem and motivation:
Currently, Stapp has very basic typings for createEvents
method. It returns an object which is a key-value object, where keys can be any string (not related to the provided EventHandlers object), and values can be any EventCreator
Solution:
Use infer
keyword available since TS2.8 to infer types automatically.
Example:
type EH<State, Payload> = (state: State, payload: Payload) => State
type EC<P> = (arg: P) => { type: string, payload: P }
declare function createEvents<
State,
Api extends { [K: string]: EH<State, any> }
>(
model: Api
): {
[K in keyof Api]: Api[K] extends EH<State, infer Payload> ? EC<Payload> : null
}
const events = createEvents({
test: (state: any, payload: number) => 123
})
events.test('sads') // Error
events.test(123) // OK
Problem
For rendering some content based on upper component props we can't use components wrapped in Form helper.
For example:
const Form = createForm(app);
const SomeStatelessComponent = ({ fields }) => <Form>
{
({ handleSubmit }) => <form onSubmit={handleSubmit}>
{ fields.map(renderField) }
<button onClick={handleSubmit}>OK</button>
</form>
}
If fields was updated fields.map(renderField)
wouldn't be called. But if something included in formSelector
was changed form would be re-rendered.
Solution
Add "extra" selector which extends formSelector
Mb something else
It should be unique and should depend on the provided name
value.
Currently, stapp-validate
accepts only a static set of validation rules. Sometimes rules have to be created dynamically, and providing an application's state to the rule itself doesn't solve the issue.
Solution
rules
field of a configuration object should accept a function:
type ValidateConfig<State> = {
+ rules: ValidationRules<State> | (state: State) => ValidationRules<State>
- rules: ValidationRules<State>
validateOnInit?: boolean
setTouchedOnSubmit?: boolean
}
reactWith events in stapp-select are calling before updates from selector fall into app state
To handle some cases we want to extend error with flags. But validate module turn this rules
validate({
rules: {
'someField': value => { error: 'Error message', flag: true }
}
})
into state without field name key
errors: {
error: 'Error message'
}
Problem and motivation:
Currently, an application starts when created. That forces the developer to create wrappers to delay the initialization. Also, there is no way to "destroy" an application (force unsubscribe all listeners and let the GC do its work). Finally, "dependencies" mechanism looks utterly useless.
Solution:
dependencies
field from createApp
config.init
and destroy
.init
method should initialize modules with provided dependencies, start an engine and dispatch a special init
event.destroy
method should unsubscribe all store subscriptions and dispatch a special destroy
event.It should dispatch when ready
promise resolves.
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.