概要
ReactとReactNativeでスタイルの設定方法が似ているが微妙に違ったので覚え書き。
React.Componentのrenderメソッド内、あるいはfunctionの返り値でJSXを使うことを想定してます。
ECMAScript6の文法で書いているつもりです。
やりたいこと
cssを利用せず(ReactNativeの場合cssは使えない)、jsファイル内にスタイルを定義、さらにcssのように複数のスタイルを適用したい
実装
style定義
React
スタイルはObjectで定義する
const styles = {
hoge: {
color: '#0D2645',
backgroundColor: '#3E62B8',
},
fuga: {
color: '#FFAC5E',
},
};
styleのキーはcamelCase。'-'が使えないので注意。
オブジェクト、ハッシュ、連想配列、辞書等で列挙する際に、最後の項目にカンマをつけることをケツカンマといいますが、Gitで追記した時にケツカンマをつけていないと、前行の本質的な動作の変更がないにもかかわらず、合わせて2行(変更は削除・追加となるので実際は3行)の変更になってしまい、良くないとされています。
GitHubだと行の中の部分的なdiffを出してくれたりするのでケツカンマなくてもいいかも。
ReactNative
スタイルはReactNativeのStyleSheetを用います。
import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({
hoge: {
color: '#0D2645',
backgroundColor: '#3E62B8',
},
fuga: {
color: '#FFAC5E',
},
});
this.hoge.fuga = 'piyo'
というのがあった時に
const { fuga } = this.hoge;
console.log(fuga) // 'piyo'
のように、オブジェクトのプロパティをプロパティのキーと同名の変数で取り出すのを分割代入っていうらしいです。最近知りました。個人的にはちょっとわかりにくい気がします。
Reactでも
import { Component } from 'react';
で目にしますね。
単一スタイル適用
React
return (
<div style={styles.hoge}>React</div>
)
ReactNative
return (
<Text style={styles.hoge}>ReactNative</Text>
)
「おっ。全く同じに書ける。」と思い、複数適用の方法を調べると、ObjectとStyleSheetで実装が違うのでハマりました。
結論からいうと以下の通りです。
複数スタイル適用
React
return (
<div style={Object.assign({}, styles.hoge, styles.fuga)}>React</div>
)
ReactNative
return (
<Text style={[styles.hoge, styles.fuga]}>ReactNative</Text>
)
ReactNativeは専用のモジュールがあるのでやさしいですね。今まで単独だったものをListにするだけ。
一点、ReactNative公式のドキュメントにStyleSheetのflattenメソッドが定義されているのが気になりますが…。
一方、Reactは手動でObjectの合成をしています。用いるのはObject.assign()。このメソッドを使うことでobjectがmergeされます。 ただし、一つポイントがあって、assignメソッドは第一引数のオブジェクトを変更してしまうので、第一引数を空のオブジェクトにしておかないと、他の箇所で意図しないスタイルになってしまうので気を付けてください。
React: 配列を与えたい場合
return (
<div style={Object.assign({}, ...[styles.hoge, styles.fuga])}>React</div>
)
...Arrayの"..."はスプレッド演算子といい、配列の中身を取り出す事ができます。
React: 共通化
冷静に考えてfor文等のイテレーション内でassignで新しくオブジェクトを作ると大変なことになるので、既にそのスタイルオブジェクトが生成されてないか確認する仕組みが必要でしょうね。
class
export default class StyleManager {
constructor(styles) {
this.styles = styles;
}
findKeyString(keyString) {
if (keyString in this.styles) {
return this.styles[keyString];
} else {
return {};
}
}
getStyle(args) {
if (arguments.length == 1) { // arguments: 引数のArrayのようなもの。可変長引数を扱う
return this.findKeyString(arguments[0]);
} else if (arguments.length > 1) {
const keys = [...arguments];
const keyString = keys.join('_'); // Stringであることを前提にしてます
if (keyString in this.styles) {
return this.styles[keyString];
} else {
const stylesArray = keys.map( name => {
return this.findKeyString(name);
});
const newStyle = Object.assign({}, ...stylesArray);
this.styles[keyString] = newStyle;
return newStyle;
}
} else {
return {};
}
}
}
import { Component } from 'react';
import StyleManager from './style_manager_class';
const styles = {
hoge: {
color: '#0D2645',
backgroundColor: '#3E62B8',
},
fuga: {
color: '#FFAC5E',
},
};
export default class ExampleUsingClass extends Component {
constructor(props) {
super(props);
this.state({
text: 'class',
});
this.styleManager = new StyleManager(styles);
}
render() (
return(
<div style={this.styleManager('hoge', 'fuga')}>{this.state.text}</div>
);
)
}
singleton
全体で同じstyleを共有する場合はSingletonを用いるとよいかも?
singletonはクラスではなくインスタンスをexportすることによって、そのインスタンスをimportした全ての場所で同じものを参照できるという使い方が大事になってきそうなやつです。
class StyleManager {
constructor() { // あらかじめ定義しておく
this.styles = {
hoge: {
color: '#0D2645',
backgroundColor: '#3E62B8',
},
fuga: {
color: '#FFAC5E',
},
};
}
findKeyString(keyString) {
if (keyString in this.styles) {
return this.styles[keyString];
} else {
return {};
}
}
getStyle(args) {
if (arguments.length == 1) {
return this.findKeyString(arguments[0]);
} else if (arguments.length > 1) {
const keys = [...arguments];
const keyString = keys.join('_');
if (keyString in this.styles) {
return this.styles[keyString];
} else {
const stylesArray = keys.map( name => {
return this.findKeyString(name);
});
const newStyle = Object.assign({}, ...stylesArray);
this.styles[keyString] = newStyle;
return newStyle;
}
} else {
return {};
}
}
}
export default new StyleManager();
import { Component } from 'react';
import styleManager from './style_manager_singleton';
export default class Example extends Component {
constructor(props) {
super(props);
this.state({
text: 'singleton',
});
}
render() (
return(
<div style={styleManager('hoge', 'fuga')}>{this.state.text}</div>
);
)
}
まとめ
雰囲気で書いたので、やってみないとわからない部分がありますが、だいたいこんな感じです。
あまりstyleを外部で管理するものではない、と同僚に言われたので、singletonパターンを使うのはよくないかもしれません。
色情報、サイズ情報を別途定数で管理しつつ、styleは各Component内で管理するのが良いかも。
正直な話、私Javascript初心者でして、この言語はわからないことが多すぎますね。プロジェクトごとに言語使用が違ったりしていて混乱の極みですね。
ESLintでもAirbnbの設定を使えということが多いですが、中括弧やJSX使う中で2スペースは狭すぎでPython使う人間としてはやっぱり4スペースがいいなぁと思って書き換えるのですが。
コーディングスタイル云々はさておき、最近はほぼECMAScript6, 7(非同期処理のasync/awaitたのしい!)で統一されているので割と楽しいかなぁとも思います。