The name useProxy
was inspired by React useState.
Just like useState
, it is mainly designed to work with React applications.
Unlike useState
, which only works with React functions; useProxy
mainly works with React classes.
useProxy
is the successor of SubX, which is similar to MobX.
It allows you to maintain your app state in OOP style.
I am not saying that OOP style is the best practice for React development.
But if do want to code your React app in OOP style, you should give this library a try.
It supports TypeScript very well.
yarn add @tylerlong/use-proxy
import {useProxy} from '@tylerlong/use-proxy';
import {Component} from '@tylerlong/use-proxy/build/react';
class Store {
count = 0;
increase() {
this.count += 1;
}
}
const store = useProxy(new Store());
class App extends Component<{store: Store}> {
render() {
const store = this.props.store;
return (
<div>
<span>{store.count}</span>
<button onClick={() => store.increase()}>+</button>
</div>
);
}
}
import {useProxy} from '@tylerlong/use-proxy';
import {ProxyEvent} from '@tylerlong/use-proxy/build/models';
class Store {}
const store = useProxy(new Store());
store.__emitter__
is an EventEmitter
which will emit events about read/write to store. You can subscribe to events:
store.__emitter__.on('event', (event: ProxyEvent) => {
// do something with event
});
The signature of run
is
function run<T>(
proxy: ProxyType<T>,
func: Function
): [result: any, isTrigger: (event: ProxyEvent) => boolean]
proxy
is generated fromuseProxy
method:const proxy = useProxy(store)
.func
is a function which readsproxy
.result
is the result offunc()
.isTrigger
is a function which returnstrue
if anevent
will "trigger"func()
to have a different result.- when it returns true, most likely it's time to run
func()
again(because you will get a different result from last time).
- when it returns true, most likely it's time to run
When you invoke run(proxy, func)
, func()
is invoked immediately.
You can subscribe to proxy.__emitter__
and filter the events using isTrigger
to get the trigger events (to run func()
again).
For a sample usage of run
, please check ./src/react.ts.
Another example is the implementation of the autoRun
utility method. You may find it in ./src/index.ts.
The signature of autoRun
is
function autoRun<T>(
proxy: ProxyType<T>,
func: () => void,
decorator?: (func: () => void) => () => void
): {start: () => void; stop: () => void}
proxy
is generated fromuseProxy
method:const proxy = useProxy(store)
.func
is a function which readsproxy
.decorator
is a method to change run schedule offunc
, for example:func => _.debounce(func, 10, {leading: true, trailing: true})
start
andstop
is to start and stopautoRun
.
When you invoke start()
, func()
is invoked immediately.
func()
will be invoked automatically afterwards if there are trigger events from proxy
which change the result of func()
.
Invoke stop
to stop autoRun
.
For sample usages of autoRun
, please check ./test/autoRun.spec.ts.
- It only monitors
get
andset
of properties. It doesn't monitordelete
,has
andkeys
. Because in 99.9% cases,get
&set
are sufficient to monitor and manage data. - You cannot proxy some built-in objects, such as
Set
&Map
. run
andautoRun
only support sync methods. for async methods, make sure that the async part is irrelevant because it won't be monitored.
- cache data for getter functions to make it faster, just like what I did in SubX project
- When is
typeof path === 'symbol'
? - Support React Hooks https://reactjs.org/docs/hooks-intro.html
- I think I mean function style react components
- Native objects 会报错,比如说
window.speechSynthesis.getVoices()
autoRun
逻辑上有漏洞。比如说我想保存一个对象。一开始这个对象的property不全。后来全了。但是新增的props并不被monitor。- 一个workaround是把property的值设为null。
- 不设为undefined,因为json不支持,持久化会有问题。 不过这个问题和本项目无关
- 一个workaround是把property的值设为null。
- 如果有循环引用的结构,会报错
Uncaught RangeError: Maximum call stack size exceeded
- every
emitter.on()
must have a correspondingemitter.off()
. Otherwise there will be memory leak.- you also don't have to
on
andoff
again and again. Sometimes you juston
and let it on until user explicit it request it to be off.- check the source code of
autoRun
.
- check the source code of
- you also don't have to
- rewrite some emitter.on to promise.
- the idea is great, but it will turn the library from sync to async, which will cause unexpected consequences.
React.render
,EventEmitter.on
,rxjs.observable.next
are all sync, there must be a good reason to stay with sync.