Preact: doubt about editing returned raw VNodes

Created on 12 Oct 2017  路  5Comments  路  Source: preactjs/preact

Im trying to edit raw 'h' nodes before they get constructed

Hope u get a bit of background how im trying to do that
image

Im trying to create a @withStyle(cssObj) decorator using a @afterMethod Join Point with kaop-ts to plug a <style> ... content </style> inside some component before it gets evaluated by preact.

Because this occurs in runtime im trying to dynamicly edit component's render() hyperscript result before it become a VNode placing a preact_1.h("style", null, scopedRules))); as the last children.

My question is if there is somehow a way more friendly to edit hyperscript rather than str.replace(regexThatSearchOnHyperScriptBodyFunctionTheLastChildren, "preact_1.h("style", null, scopedRules)))") :\

The resulting implementation will look like this:

import { h, Component } from "preact";
// styles will be replaced with component scoped selectors like angular 2+ does
const styleContent = `
  button {
    color: gray;
  }
  button:hover {
    color: white;
  }
`;

export class MyButton extends Component {

  @withStyle(styleContent)
  render(props) {
    return (
        <button>{props.children}</button>
    )
  } 
}

or

export const MyButton = withStyle(styleContent)(props => (
  <button>{props.children}</button>
));

Most helpful comment

working decorator implementation:

import { h, Component } from "preact";
import { afterMethod } from "kaop-ts";

export const stylesheet = (styleContent: string) =>
afterMethod<Component<any, any>, "render">((meta) => {

  // remove all spaces, eols
  styleContent = styleContent.replace(/(\r\n\s|\n|\r|\s)/gm, "");

  // prefix all selectors to make stylesheet 'scoped'
  styleContent = styleContent.replace(
    /([^\r\n,{}]+)(,(?=[^}]*{)|\s*{)/g,
    `${meta.target.constructor.name.toLowerCase()} $1$2`
  )

  // add stylesheet to vnode
  meta.result = h(
    meta.target.constructor.name, null,
    [ meta.result, h("style", { 'scoped': true }, styleContent) ]
  );
});

All 5 comments

basically im asking for a way to 'deserialize' VNodes in hscript again :((

worth?
image

Would it be sufficient to instead mutate the resulting VNodes created by an unmodified h()?
I don't have time to write a decorator, but here'd be the wrapper function:

function withStyle(fn) {
  return function() {
    let vnode = fn.apply(this, arguments);
    vnode.children = [ h('style', /*..etc..*/) ].concat(vnode.children)
    return vnode;
  };
}

Awesome answer, thanks

working decorator implementation:

import { h, Component } from "preact";
import { afterMethod } from "kaop-ts";

export const stylesheet = (styleContent: string) =>
afterMethod<Component<any, any>, "render">((meta) => {

  // remove all spaces, eols
  styleContent = styleContent.replace(/(\r\n\s|\n|\r|\s)/gm, "");

  // prefix all selectors to make stylesheet 'scoped'
  styleContent = styleContent.replace(
    /([^\r\n,{}]+)(,(?=[^}]*{)|\s*{)/g,
    `${meta.target.constructor.name.toLowerCase()} $1$2`
  )

  // add stylesheet to vnode
  meta.result = h(
    meta.target.constructor.name, null,
    [ meta.result, h("style", { 'scoped': true }, styleContent) ]
  );
});
Was this page helpful?
0 / 5 - 0 ratings