Skip to content

Instantly share code, notes, and snippets.

@3mcd
Last active December 19, 2018 19:03
Show Gist options
  • Save 3mcd/ba0e24a74a8aea67188f1d58e9b4cf30 to your computer and use it in GitHub Desktop.
Save 3mcd/ba0e24a74a8aea67188f1d58e9b4cf30 to your computer and use it in GitHub Desktop.
import React, { Component, Fragment } from "react";
import { render } from "react-dom";
// Constrain
// A component that takes a render prop (children) and passes a proxied
// version of remaining props to the render prop function. When a prop is
// accessed in the render prop function, Constrain will re-render when
// that prop changes in the future.
class Constrain extends Component {
constructor() {
super();
this.subscriptions = new Set();
}
setProxy(allProps) {
const { children, ...props } = allProps;
if (typeof this.revoke === "function") {
this.revoke();
}
const { proxy, revoke } = Proxy.revocable(props, {
get: (target, key) => {
if (target.hasOwnProperty(key)) {
this.subscriptions.add(key);
}
return target[key];
}
});
this.proxy = proxy;
this.revoke = revoke;
}
componentWillMount() {
this.setProxy(this.props);
}
componentWillUnmount() {
if (typeof this.revoke === "function") {
this.revoke();
}
}
shouldComponentUpdate(nextProps) {
this.setProxy(nextProps);
for (let key of Object.keys(nextProps)) {
if (
key !== "children" &&
this.props[key] !== nextProps[key] &&
this.subscriptions.has(key)
) {
return true;
}
}
return false;
}
render() {
return this.props.children(this.proxy);
}
}
// Simple example:
function Root(props) {
return (
<Fragment>
{/* will only re-render when `a` changes */}
<Constrain {...props}>{({ a }) => (<span>{a}</span>)}</Constrain>
{/* will only re-render when `b` changes */}
<Constrain {...props}>{({ b }) => (<span>{b}</span>)}</Constrain>
</Fragment>
);
}
// Full example:
class App extends Component {
constructor() {
super();
this.state = { a: 0, b: 0 };
}
render() {
console.log(`App render`);
return (
<Fragment>
{/* adding `b` will cause this component to re-render when the second button is clicked */}
<Constrain {...this.state}>
{({ a }) =>
console.log(`Render prop: ${a}`) || (
<button onClick={() => this.setState({ a: a + 1 })}>
A: {a}
</button>
)
}
</Constrain>
<button onClick={() => this.setState({ b: this.state.b + 1 })}>
B: {this.state.b}
</button>
</Fragment>
);
}
}
render(<App />, document.getElementById("app"));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment