web-dev-qa-db-ja.com

React:オブジェクトプロトタイプを拡張する場所

純粋なReact create-react-app を使用してアプリケーションを作成しました。Stringクラスを拡張して1つ以上のコンポーネントで使用したい。 例えば:

String.prototype.someFunction = function () {
    //some code
}

(オブジェクトプロトタイプの拡張の詳細については、 この質問 をご覧ください。)

はい、コンポーネントの横に定義して、その中で使用できます。しかし、最善かつ最もクリーンな方法は何ですか?

class methodまたはcomponentDidMountの内部か、他に何かありますか?

編集:

React(またはJavaScriptで)でオブジェクトプロトタイプを拡張しても「OK」ですか?

11
Ahmad Maleki

TLDR回答:どこにもありません!

-微妙な回答-

私がやろうとしていることは、JavaScriptで非常に一般的なタスクであるStringクラスのような純粋なJavaScriptクラスを拡張することです

React(またはJavaScriptで)でオブジェクトプロトタイプを拡張することは「OK」ですか?

JavaScriptでネイティブプロトタイプを拡張/変更することは 物議を醸すトピック であり、あなたが言ったこととは逆に、ほとんどのプロの開発者が頻繁に行うことではありません。 一般的なコンセンサスネイティブJSプロトタイプの拡張は避けるべきプログラミングのアンチパターンです カプセル化の原則を破り、グローバル状態を変更するためです。ただし、多くのルールと同様に、まれな例外がある場合があります。たとえば、あなたは本番品質である必要のないおもちゃプロジェクトに取り組んでいる、あなたがそのコードベースに触れる唯一の開発者である、またはあなたのコードが他の誰にも依存しないことは決してありません。

本当に正当な理由があり、何をしているのかを本当に理解していて、ランタイム環境と依存関係のネイティブデータ型/動作に対する変更の潜在的な結果を完全に認識している場合は、おそらくいくつかの有効な使用法を見つけるでしょうこの練習のケース。しかし、おそらくそうではないか、少なくともそれほど頻繁ではありません。ほとんどないように。

便利さ/構文糖度のすぐ後である場合は、ユーティリティ関数(lodash、underscore、またはramdaなど)を取り入れて、関数合成の練習を学ぶ方がよいでしょう。しかし、本当にオブジェクト指向のパラダイムに取り組んでいる場合は、ネイティブデータタイプを変更するのではなく、単にサブクラス化する必要があります

したがって、次のようにクラスのプロトタイプを変更するのではなく、

String.prototype.somethingStupid = function () {
  return [].map.call(this, function(letter) {
    if ((Math.random() * 1) > .5) return letter.toUpperCase()
    else return letter.toLowerCase()
  }).join('')
}

console.log('This is a silly string'.somethingStupid())

次のように、サブクラスを作成します(ES6クラス構文でのみ機能します)。

class MyString extends String {
  constructor(x = '') {
    super(x)
    this.otherInstanceProp = ':)'
  }
  
  somethingStupid() {
    return [].map.call(this, function(letter) {
      if ((Math.random() * 1) > .5) return letter.toUpperCase()
      else return letter.toLowerCase()
    }).join('')
  }
}

const myStr = new MyString('This is a silly string')
console.log(myStr)
console.log(myStr.valueOf())
console.log(myStr.somethingStupid() + ', don\'t you think?')

このサブクラスは、すべての点で組み込みのStringのように機能しますが、もちろん、StringリテラルのようなMyStringリテラルを作成することはできません。

Create-react-appを使用して純粋なReactアプリケーションを作成しました。Stringクラスを拡張して1つ以上のコンポーネントで使用したいと思います...はい、コンポーネントの横に定義して、内部で使用します。しかし、最善かつ最もクリーンな方法は何ですか?...クラスメソッドとして、またはcomponentDidMountなどの内部に記述すべきですか?

組み込みプロトタイプを変更すると(String.prototypeなどを変更することにより)、アプリケーションのグローバル状態が変更されるため、他のコードが実行される前に1回だけ実行する必要があります(設定しているため)後に実行されるすべてのコードに対して文字列がどのように動作するかのグローバルな状態)。したがって、Reactコンポーネントインスタンスメソッド内から組み込みプロトタイプを変更しても、あまり意味がありません。

汚れた行為を行う場合は、変更するネイティブタイプごとに個別のモジュールを作成し、それらのモジュールをsrc/lib/extend-built-ins/などのどこかに保持してから、importを保持することをお勧めします。 src/index.jsの一番最初のものとして。何もエクスポートする必要はありません。 import src/lib/extend-built-ins/String.jsを実行すると、コードが実行され、グローバル状態が変化します。これにより、少なくともまともな組織が提供され、アプリケーションの残りのコードが実行される前に、アプリケーション環境が完全に変更されます。そうすれば、拡張型をどこからでもインポートすることを考えずに、アプリケーション全体で拡張型を使用できます。

サブクラス化ルート(class MyThing extends NativeThing)を使用する場合は、src/lib/native-subclasses/などの別のモジュールでカスタムクラスを同様に定義することをお勧めします。ただし、この場合は、クラスコンストラクタを、使用するすべてのモジュールにimportする必要があります。

ただし、他の人や将来の自分が簡単に理解できる、クリーンでテスト可能な、リファクタリング可能なコードを開発したい場合は、このようなことはすべきではありません。代わりに、Reactとそのエコシステムの関数型プログラミングの原則を採用することを検討してください。誰でもすぐに純粋な関数を読み取って理解できるため、ハードに頼るのではなく、それらを使用してデータと状態の変換を実行します。グローバルオブジェクトの変更などのハックを追跡します。この1つの小さなハックを理解するのはかわいくて些細なことかもしれませんが、プロジェクトで一度でもそれを行うと、自分や他の人が追加のショートカットやアンチパターンを使用することを促進および奨励します。

11
Derrick Beining