rescript-react-native icon indicating copy to clipboard operation
rescript-react-native copied to clipboard

Easy syntax for stylesheet without Js.t

Open MoOx opened this issue 6 years ago • 11 comments

The problem

For new comers, having to use {"title": ...} & styles##title isn't straightforward.

Considered solution

We cannot use record in StyleSheet.create so we could write a simple ppx to help

[@react.stylesheet]
let styles = StyleSheet.create({title: ...})

// which will allow
styles.title;

I discussed with @bloodyowl about this & he told me a ppx solution should be easy to do.

Do you think that's a good idea to investigate?

(note: this will be optional & shouldn't be a breaking change in any sort)

Alternatives solutions

I don't see any.

MoOx avatar Mar 30 '20 14:03 MoOx

@MoOx Does something similar to https://github.com/reasonml-labs/bs-css#usage-for-bs-css-dom have some compatible limit?

tienle avatar Apr 13 '20 08:04 tienle

@tienle Nope. I just have to do with the way we define Js.t for "stylesheet" instead of record. Also we are using labeled arguments to ensure zero-cost bindings & bs-css is using function (like we were doing in bs-react-native)

MoOx avatar Apr 13 '20 14:04 MoOx

@MoOx I see. Currently, I'm putting styles into a module like following:

module EmojiIcon = {
  module Styles = {
    open Style;

    let container =
      style(
        ~width=50.->dp,
        ~height=50.->dp,
        ~justifyContent=`center,
        ~alignItems=`center,
        ~margin=auto,
        ~backgroundColor="red",
        ~borderRadius=5.0,
        (),
      );

    let text = textStyle(~fontSize=28.0, ~lineHeight=28.0, ());
  };

  [@react.component]
  let make = (~emoji, ~style=?) => {
    <View style=Styles.container>
      <Text style=Styles.text> emoji->string </Text>
    </View>;
  };
};

Hence, Js.t thing is not bothering me much , and generated JS output is efficient as plain JS object since BS v.7 as well.
The label arguments with ~ characters all over around are not quite pleasant to read in comparison to the RN Javascript version. However, now I understand why it should be that way in term of zero-cost binding and type-safety :)

BTW, I'm starting to learn Reasonml and Reason RN, you are doing the great work. Thank you for keeping this up! ❤️ 💯

tienle avatar Apr 13 '20 15:04 tienle

I wonder if we actually need StyleSheet.create. Its current implementation looks like this:

  /**
   * Creates a StyleSheet style reference from the given object.
   */
  create<+S: ____Styles_Internal>(obj: S): $ReadOnly<S> {
    // TODO: This should return S as the return type. But first,
    // we need to codemod all the callsites that are typing this
    // return value as a number (even though it was opaque).
    if (__DEV__) {
      for (const key in obj) {
        StyleSheetValidation.validateStyle(key, obj);
        if (obj[key]) {
          Object.freeze(obj[key]);
        }
      }
    }
    return obj;
  }

I.e. in dev mode, it validates the styles and freezes the style values. Apart from that, it just returns its input.

I think neither the validation nor the freezing make much sense when working in Reason.

So maybe we should just recommend @tienle's approach of putting the styles into a module.

cknitt avatar Sep 15 '20 18:09 cknitt

Depending on the platform, implementation differ. For the web the implementation is much more complex as this split this into atom classes etc. Not sure we want to recommend approach depending on the targeted platform. Also I think in the past this was different (returning {[key: string]: number} if I am correct) and this could change again ((or maybe not, I have no clue on the future of StyleSheet APIs)).

MoOx avatar Sep 16 '20 08:09 MoOx

With new ReScript syntax, current state is imo worst than ever... image

MoOx avatar Nov 27 '20 16:11 MoOx

Does anyone have some skills so we could write a ppx that could transform this

@reactnative.styles
let styles = { key: style() }

as

type styles = { key: ReactNative.Style.t }
let styles = StyleSheet.create({ key: style() })

Is this a good idea in the first place?

MoOx avatar Nov 27 '20 16:11 MoOx

@MoOx Is there any benefits of using StyleSheet.create rather than a module?

e.g.


module Styles = {
  let style1 = style();
};

cem2ran avatar Nov 28 '20 17:11 cem2ran

See 2 comments above https://github.com/reason-react-native/reason-react-native/issues/667#issuecomment-693267814

MoOx avatar Nov 28 '20 19:11 MoOx

I've done some digging and am implementing a ppx rewriter for react-native-reanimated (especially the worklet syntax in Reanimated 2).

It seems that the most maintainable way would be to use Ppxlib, however, that also limits our PPX to use the extension interface

Similar to what is stated above, but more akin to:

%%stylesheet(let styles = { key: style() })

This will rewrite to

// numbered to allow multiple definitions within the same module
type reactnative_stylesheet__0 = { key: ReactNative.Style.t }
let styles: reactnative_stylesheet__0 = ReactNative.StyleSheet.create({ key: style() })

The other issue will be that this can only be used on "structure items", which means


%%stylesheet(let style1 = { key: style() }) // okay

module A = {
  %%stylesheet(let style2 = { key: style() }) // okay

  let make = (~something) => {
    %%stylesheet(let style3 = { key: style() }) // doesn't work
    something()
  }
}

As somewhat of a side note, the Reason and OCaml syntax generated by this extension looks a lot nicer, essentially:

// Reason
let%stylesheet styles = { key: style() };
(* OCaml *)
let%stylesheet styles = { key = style () }

Since writing a PPX will mean integrating into the OCaml compiler framework, we'll need to:

  1. add esy as a dev dependency to enable any package consumer to be able to build the PPX themselves
  2. add a postinstall script to build and expose the PPX in the package
  3. instruct the package consumer to add to bsconfig.json:
{
  "ppx-flags": [..., ["rescript-react-native/ppx.exe", "-as-ppx"], ...]
}

diaozheng999 avatar Mar 28 '21 12:03 diaozheng999

See https://github.com/rescript-react-native/rescript-react-native/pull/739 for more info about what is next to do

MoOx avatar May 21 '21 06:05 MoOx

#769 will make this kind of fixed.

MoOx avatar Aug 18 '22 07:08 MoOx