r-ninja
Version:
r-ninja watches for changes in JSX expressions and updates UI with very less glue code.
66 lines (65 loc) • 1.9 kB
JavaScript
import React, { useContext, useState, useEffect } from "react";
import { Watcher } from "./watcher";
let refreshPlanned = false;
export const check = () => {
if (refreshPlanned) {
return;
}
refreshPlanned = true;
requestAnimationFrame(() => {
Watcher.ROOT.check();
refreshPlanned = false;
});
};
export const NinjaContext = React.createContext(null);
export class PropsWatcher extends React.Component {
constructor(props) {
super(props);
this.watcher = null;
this.refresh = () => {
this.setState({
id: this.state.id + 1
});
};
this.state = {
id: 0
};
}
shouldComponentUpdate(nextProps, nextState) {
return (this.state.id !== nextState.id || this.watcher.isDestroyed)
|| Object.keys(nextProps).reduce((p, k) => {
return p || (k !== 'render' && this.props[k] !== nextProps[k]);
}, false);
}
componentWillUnmount() {
this.watcher && this.watcher.destroy();
}
watch(fn) {
return this.watcher.watch(fn, this.refresh).value;
}
render() {
return React.createElement(NinjaContext.Consumer, null, ((parent) => {
parent = parent || Watcher.ROOT;
this.watcher = this.watcher || parent.create();
this.watcher.clear();
return this.props.render(this.watch.bind(this));
}));
}
}
export function useWatcher() {
const parent = useContext(NinjaContext);
const [change, onChange] = useState({});
let [watcher] = useState(() => parent.create());
watcher.clear();
useEffect(() => {
return () => {
watcher.destroy();
};
}, [watcher]);
return {
watch: (fn) => {
return watcher.watch(fn, () => onChange({})).value;
}
};
}
;