現在、非公式のReact Native SDKを作っているのですが、公式JavaScript SDKがES5ベースで作られているのに対して、React Native SDKはTypeScript/ES6ベースになっています。
一番大きな違いとしてはクラスを使って構築されていることです。その動作差異で苦労した点を紹介します。
やりたいこと
JavaScript SDKのようにデータストアのクラスを生成し、そのインスタンスで行のデータを作りたい。
const Item = ncmb.DataStore('ReactNative'); const item = new Item(); // ここ! item .set('msg', 'Hello World') .save() .then((data) => { })
これを実現しようと思うと、DataStoreクラスのクラス名に ReactNative というラベルを持たせる必要があります。
クラス変数について
JavaScriptのクラスにはクラス変数(スタティック変数)がありません。TypeScriptでは実現できているのですが、これはクラス全体で共有されてしまいます。
class Hoge {} const aa = Hoge; const bb = Hoge; aa.test = 'test1'; bb.test = 'test2' console.log(aa.test) // -> test2
そのため、クラス変数に持たせることができません。
解決策1:evalを使う
evalを使って動的にクラスを作成する方法があります。
const getDataStore = (name) => { const d = eval(`class ${name} extends DataStore { constructor() { super(); this.className = ${name}.className; } }`); d.className = name; return d; } const Item1 = getDataStore('Item1') const Item2 = getDataStore('Item2') const item = new Item1(); console.log(item.className); // -> Item1
この方法はNode.jsであれば問題ないのですが、React Nativeの場合はevalを別な環境(blob上)で行うようで、DataStoreクラスがないというエラーが出てしまいます。
解決策2:関数を使う
そこで考えたのが継承を使う方法です。DataStoreを継承したクラスを定義した場合、クラス変数は別空間で管理されます。また、関数の中でクラスを継承して返却する場合、クラス名は動的でなくとも構いません。
DataStore = (name?: string) => { if (!name) { throw new Error('Please set the className') } return class Data extends DataStore { static ncmb = this constructor(fields?: object) { super(Data.ncmb, fields) this.className = name } } }
この方法であれば、動的にクラス変数を変えつつ、JavaScript SDKと同じような動作が実現できます。
まとめ
今回の方法を使えば、JavaScript SDKもクラスベース(ES6ベース)にするのもさほど難しくなさそうです。クラスになれば継承も容易になりますし、JavaScript SDKの拡張もしやすくなるでしょう。