Android
iOS
reactjs
reactnative

【React-Native】StyleSheetについて

More than 1 year has passed since last update.

はじめに

React Native では stylesにスタイルを定義してかいているのですが、最近、Webの方でもReact Nativeと同じようにCSS in JSで書きだしてから、React Nativeを書く時にStyleSheet.createを忘れてしまう事案が発生しているので戒めに書きます。
あまり大した内容でなくすみません・・・。

React.js,ReactNativeでもこう書いてしまっていた・・・
const styles = {
  container: {
      marginLeft: '1rem',
      backgroundColor: COLORS.BG_TEAL,
  },
};
class App extends Component {
・・・・
本来ReactNativeではこう書く
const styles = StyleSheet.create({
   container: {
       flex: 1,
       backgroundColor: COLORS.BG_TEAL,
   },
});

class App extends Component {
・・・

そもそもStyleSheet.createは必要?

動作上は不要です。ですので、StyleSheet.createしなくとも動きますし、しない方がプレーンなオブジェクトで便利だったりしますが・・・・・・。

StyleSheet.createした方がいいよ!

StyleSheet.createをすることによって、オブジェクトはidで返るようになります。
これによってパフォーマンスの向上が見込めるので極力はやっておいた方がよいです。

StyleSheet.createする場合
const styles = StyleSheet.create({
  container: {
     flex: 1,
  },
})

console.log(styles.container); // 6 などのプリミティブなnumberが返る
StyleSheet.createしない場合
const styles = {
  container: {
     flex: 1,
  },
};

console.log(styles.container); //ちゃんとオブジェクトが返る。{ flex: 1, }

プリミティブからオブジェクトへの復元

こう・・・、数値で返ってこられるとデバッグの時にこれなんやねんってなって困ることがあります。そこでflattenを使うことで元に戻すことができます。

const styles = StyleSheet.create({
  container: {
     flex: 1,
  },
})

console.log(styles.container); // 6

const _container = StyleSheet.flatten(styles.container);
console.log(_container); //{ flex:1, }

ところが

console.log(Object.getOwnPropertyDescriptor(_container, 'flex')); 
/*
 configurable:false,
 enumerable:true
 value:1
 writable:false
*/

ディスクプリタをみてみるとwritable: falseになっているため書き換えできませんので要注意です。
さらにはconfigurableもfalseなので消すこともできません。

オブジェクトの統合とプロパティの上書き

汎用的なコンポーネントを作成した時には、styleのオーバーライドをしたくなることがあります。
これはradiumと同じ記法でオーバーライドすることで解決できます。
ここでもflattenは活躍します(中身を確認する時は・・・)。

const styles = StyleSheet.create({
    container: {
        flex: 1,
        margin: 30,
    },
    _container: {
        flex: 2,
        padding: 10,
    }
});

console.log(StyleSheet.flatten([styles.container, styles._container]));
// { flex: 2, margin: 30, padding: 10, }

convenience properties

他には便利なプロパティも持っています。

absoluteFill

console.log(StyleSheet.absoluteFill); // 2
console.log(StyleSheet.flatten(StyleSheet.absoluteFill));
/*
  bottom: 0,
  left: 0,
  position: 'absolute',
  right: 0,
  top: 0,
*/

これはプリローダーやモーダルダイアログを出すのに良い感じに動いてくれます。

const styles = StyleSheet.create({
    container: {
        flex: 1,
    },
    _container: {
        flex: 2,
    },
    overlay: {
        backgroundColor: 'rgba(0, 0, 0, 0.7)',
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
    },
    preloader: {
        backgroundColor: 'rgba(255, 255, 255, 0.8)',
        justifyContent: 'center',
        alignItems: 'center',
        height: 200,
        width: 200,
        borderRadius: 3,
    },
});


const App = (props) => (
        <View style={styles.container}>

            {/* コンテンツ */}
            <Main {...props} />

            {/* オーバーレイ */}
            <View style={StyleSheet.absoluteFill} >
                <View style={styles.overlay} >
                    <View style={styles.preloader}>
                        <Text>ぐるぐる回るやつ</Text>
                    </View>
                </View>
            </View>
        </View>
);

absoluteFillObject

absoluteFillと中身は同じですがこちらはオブジェクトで返ります。

console.log(StyleSheet.absoluteFillObject);
/*
  bottom: 0,
  left: 0,
  position: 'absolute',
  right: 0,
  top: 0,
*/

console.log(
    Object.getOwnPropertyDescriptor(
        StyleSheet.absoluteFillObject, 'top'
    )
);
/*
 configurable:false,
 enumerable:true
 value:object,
 writable:false,
*/

具体的な使い方としては

const styles = StyleSheet.create({
    container: {
        flex: 1,
        margin: 30,
        ...StyleSheet.absoluteFillObject,
    },
});
console.log(StyleSheet.flatten(styles.container));

/*
  bottom: 0,
  left: 0,
  position: 'absolute',
  right: 0,
  top: 0,
  flex: 1,
  margin: 30,
*/

となります。

hairlineWidth

それぞれのプラットフォームでもっとも薄い線を引く時に使える数値が設定されています。

iphone7Plus
console.log(StyleSheet.hairlineWidth); // 0.3333333

ただし、これは固定値ではなくプラットフォーム依存なので、

端末 数値
iphone7Plus 0.333333333
iphone7 0.5
iphone5 0.5
Nexus5x 0.333333333

という感じで異なります。
横幅に依存してるんですかね?

const styles = StyleSheet.create({
   _box: {
      borderColor: '#3C3C3C',
      borderWidth: StyleSheet.hairlineWidth,
   },
});

という感じで利用することができます。

終わりに

以上で終了です!
CSS風なプロパティなんだーぐらいで納得してしまってあまりStyleSheetやStyleSheet.createを使う意義なども考えてこなかったのでいざこうやって勉強し直してみると意外と便利な機能もあるんだなと勉強になりました。
しかし、実際はStyleSheet.createさえ覚えとけばだいたい大丈夫だと思います!
StyleSheet.createルーチンすぎて、忘れやすいですが是非ともお忘れなくつけていただけますと幸いです。