Skip to content

Instantly share code, notes, and snippets.

@developit
Created January 7, 2017 23:03
Show Gist options
  • Save developit/5c7bae447e0456db907d53fb942f7b3b to your computer and use it in GitHub Desktop.
Save developit/5c7bae447e0456db907d53fb942f7b3b to your computer and use it in GitHub Desktop.
Demonstrates composition using "render callbacks" (AKA function-as-children) in Preact/React.
import { h, Component } from 'preact';
/** Example <Fetch url="/foo.json" /> compositional component.
* The key is, instead of passing virtual DOM elements as "children" to this component,
* you pass a _function_ that returns virtual DOM elements. Fetch calls that function and passes it the network data.
*/
class Fetch extends Component {
state = { loading: true };
componentDidMount() {
this.update();
}
componentDidUpdate(prevProps) {
if (this.props.url!==prevProps.url) {
this.update();
}
}
update() {
let { url, as='json' } = this.props;
this.setState({ loading:true, error:null });
fetch(url)
.then( r => r[as]() )
.then( data => this.setState({ loading:false, data }) )
.catch( error => this.setState({ loading:false, error }) );
}
render(props, state) {
// props.children[0] is just a function that returns VDOM (like a Pure Functional Component).
// instead of letting the renderer invoke it and set props, we'll just call it with our data here:
return props.children[0](state);
}
}
/** "Smart" component, akin to a Controller.
* Typically arguments/props to this come from the URL.
* Note now this is just a pure function, containing a pure function.
* We can store state in the parent, and use Fetch to re-render. Crazy!
*/
const ProfilePage = ({ id }) => (
<Fetch url={'/user/'+encodeURIComponent(id)} as="json">
{ ({ loading, data, error }) => (
<div class="some-view" data-loading={loading}>
{ error && <ErrorBox error={error} /> }
{ data && (
<div class="profile">
<h1>{data.name}</h1>
<p>{data.bio}</p>
<a href={data.url} target="_blank">{data.urlName}</a>
</div>
) }
</div>
) }
</Fetch>
);
/** A "template partial" */
const ErrorBox = ({ error }) => (
<div class="error">
<h1>We have a problem!</h1>
<pre>{error.message}</pre>
</div>
);
// magic:
render(<ProfilePage id="5" />, document.body);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment