preact composition demo 1

by Jason Miller

HTML

<script src="https://unpkg.com/preact/dist/preact.min.js"></script>

CSS

body {
	font: 14px/1.21 'helvetica neue',helvetica,arial,sans-serif;
	background: #fff;
}

Babel + JSX

const { h, cloneElement, Component, render } = preact; /** @jsx h */


/** Example <Fetch url="/foo" /> compositional component.
 *  Just to demonstrate a compositional component that requires input massaging.
 */
class Fetch extends Component {
  state = { loading: true };
  componentDidMount() {
    this.update();
  }
  componentDidUpdate(prevProps) {
    if (this.props.url!==prevProps.url) {
      this.update();  // fetch changed url
    }
  }
  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, data:null, error }) );
  }
  render(props, state) {
    // just pass all state to the child as props
    return cloneElement(props.children[0], state);
  }
}


/** "Smart" component, akin to a Controller.
 *  Typically arguments/props to this come from the URL.
 */
const ProfilePage = ({ id }) => (
  <Fetch url={'//api.myjson.com/bins/'+encodeURIComponent(id)}>
    <ProfileView you-could="pass default props here" />
  </Fetch>
);


/** "Dumb" view component, analagous to a template */
const ProfileView = ({ 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>
);


/** A "template partial" */
const ErrorBox = ({ error }) => (
  <div class="error">
    <h1>We have a problem!</h1>
    <pre>{error.message}</pre>
  </div>
);


// magic:
render(<ProfilePage id="yoxtb" />, document.body);