XHP: Guidelines

Here are some general guidelines to know and follow when using XHP. In addition to its basic usage, available methods and extending XHP with your own objects, these are other tips to make the best use of XHP.

Validation of Attributes and Children

The constraints of XHP object children and attributes are done at various times:

  • Children constraints are validated at render-time (when toString() is called explicitly or implicitly).
  • Attribute names and types are validated when the attributes are set in a tag or via setAttribute().
  • @required is validated when the required attributes are read.

This validation is on by default. You can turn it off by running the following code before using XHP:

:xhp::$ENABLE_VALIDATION=false

Use Contexts to Access Higher Level Information

If you have a parent object, and you want to give information to some object further down the UI tree (e.g., <ul> to <li>), you can set a context for those lower objects and the lower objects can retrieve them. You use setContext() and getContext()

<?hh

class :ui-myparent extends :x:element {
  attribute string text @required;
  children (:ui-mychild);

  protected function render(): XHPRoot {
    return (
      <dl>
        <dt>Text</dt>
        <dd>{$this->:text}</dd>
        <dt>Child</dt>
        <dd>{$this->getChildren()}</dd>
      </dl>
    );
  }
}

class :ui-mychild extends :x:element {
  attribute string text @required;

  protected function render(): XHPRoot {
    if ($this->getContext('hint') === 'Yes') {
      return
        <x:frag>{$this->:text . '...and more'}</x:frag>;
    }
    return
      <x:frag>{$this->:text}</x:frag>;
  }
}

function guidelines_examples_context_run(string $s): void {
  $xhp = (
    <ui-myparent text={$s}>
      <ui-mychild text="Go" />
    </ui-myparent>
  );
  $xhp->setContext('hint', $s);
  echo $xhp;
}

guidelines_examples_context_run('No');
echo "\n\n";
guidelines_examples_context_run('Yes');
Output
<dl><dt>Text</dt><dd>No</dd><dt>Child</dt><dd>Go</dd></dl>

<dl><dt>Text</dt><dd>Yes</dd><dt>Child</dt><dd>Go...and more</dd></dl>

Context is only passed down the tree at render time; this allows you to construct a tree without having to thread through data. In general, you should only call getContext() in the render() method of the child.

Common uses include:

  • current user ID
  • current theme in multi-theme sites
  • current browser/device type

Don't Add Public Methods to Your XHP Components

XHP objects should only be interacted with by their attributes, contexts, position in the tree, and rendering them. At Facebook, we haven't yet came across any situations where public methods are a better solution than these interfaces.

Use Inheritance Minimally

If you need an XHP object to act like another, but slightly modified, use composition. Categories and attribute cloning can be used to provide a common interface.

Remember No Dollar Signs

Attribute declarations look like Hack property declarations:

public string $prop;
attribute string attr;

A key difference is that there is no $ in front of the attribute name.