web-dev-qa-db-ja.com

React-破壊時のdefaultPropsとES6のデフォルトのパラメーター(パフォーマンスの問題)

私はReact私のステートレス機能コンポーネントのいずれかにデフォルト値を設定したときのパフォーマンスに関する質問に出くわしました。

このコンポーネントにはrow: falseを定義するdefaultPropsがありましたが、defaultProps末尾であり、実際には見にくくなります。したがって、デフォルトのプロパティを認識していません。そのため、関数宣言に直接移動し、パラメーターのES6デフォルト値を使用して割り当てました。

const FormField = ({
  row = false,
  ...others,
}) => {
  // logic...
};

しかし、私たちはこれについて同僚と議論しました良いアイデアかどうか。それは些細なことのように思えるかもしれませんが、デフォルト値のreactは認識しないであるため、パフォーマンスに大きな影響を与える可能性があるためです。

私はこの場合、それは些細なことだと信じています。これはブール値であり、オブジェクト/配列ではないため、reconciliation中に異なる値として表示されないためです。


しかし、より高度なユースケースを見てみましょう。

const FormField = ({
  input: { name, value, ...inputRest },
  label = capitalize(name),
  placeholder = label,
  row = false,
  meta: { touched, error, warning },
  ...others,
}) => {
  // logic...
};

ここでは、placeholderの値のベースをlabelから取得しますが、それ自体はinput.nameに基づいています。パラメータのデフォルト値を使用してES6のデストラクチュアリングを使用すると、全体を非常に簡単に記述/理解でき、魅力のように機能します。

しかし、それは良いアイデアですか?そうでない場合、どのように適切に行いますか?

38
Vadorequest

Discord #reactifluxチャンネルで何人かと話し、実際に私が探していた答えを得ました。

基本的にReactコンポーネントを使用する3つのユースケースがあり、そのうちのいくつかでは、パラメーターの破壊がパフォーマンスに影響を与えるため、内部で何が起こっているのかを理解することが重要です。

ステートレス機能コンポーネント

const MyComponent = ({ name = 'John Doe', displayName = humanize(name), address = helper.getDefaultAddress() }) => {
  return (
    <div>{displayName}</div>
  );
};

これは、ステートレスで機能的なコンポーネントです。状態はなく、Classインスタンスではなく、単純な関数であるため機能的です。

この場合、ライフサイクルはありません。componentWillMountまたはshouldComponentUpdateまたはconstructorを使用することはできません。また、ライフサイクルの管理がないため、パフォーマンスへの影響はまったくありません。このコードは完全に有効です。関数本体内でデフォルトのdisplayName値を処理することを好む人もいるかもしれませんが、結局はそれは実際には重要ではなく、パフォーマンスに影響を与えません。

ステートレスの非機能コンポーネント

(これをしないでください!)

class MyComponent extends React.Component {
    render() {
        const { name = 'John Doe', displayName = humanize(name), address = helper.getDefaultAddress() } = this.props;
        return (
            <div>{displayName}</div>
          );
    }
}

これは、ステートレスの非機能コンポーネントです。状態はありませんが、classなので「機能的」ではありません。そして、それはReact.Componentを拡張するクラスであるため、ライフサイクルを持つことになります。 componentWillMountまたはshouldComponentUpdateまたはconstructorがあります。

また、ライフサイクルがあるため、このコンポーネントの記述方法はbadです。しかし、なぜ?

簡単に言えば、ReactはdefaultProps属性を提供して、デフォルトの小道具の値を処理します。 this.propsに依存するすべてのメソッドによって呼び出されます。

前のコードスニペットは、nameおよびdisplayNameという名前の新しいローカル変数を作成しますが、このrenderメソッドのみにデフォルト値が適用されます!。 Reactライフサイクル(shouldComponentUpdateなど)のメソッドなど、すべてのメソッドにデフォルト値を適用する場合は、must)代わりにdefaultPropsを使用します。

したがって、以前のコードは実際には間違いであり、nameのデフォルト値について誤解を招く可能性があります。

同じ動作を得るために、代わりにどのように書くべきかを以下に示します。

class MyComponent extends React.Component {
    render() {
        const { name, displayName = humanize(name), address } = this.props;
        return (
            <div>{displayName}</div>
          );
    }
}

MyComponent.defaultProps = {
    name: 'John Doe',
    address: helper.getDefaultAddress(),
};

これの方が良い。名前は、定義されていない場合は常にJohn Doeになるためです。 addressデフォルト値も処理されましたが、displayName...は処理されませんでした。なぜですか?

まあ、私はまだその特別なユースケースを回避する方法を見つけていません。 displayNamenameプロパティに基づいている必要があるため、defaultPropsを定義するときにアクセスできません(AFAIK)。私が見る唯一の方法は、renderメソッドで直接処理することです。おそらくもっと良い方法があります。

addressプロパティにはこの問題はありません。MyComponentプロパティに基づいていないが、小道具を必要としない完全に独立した何かに依存しているためです。

ステートフル非機能コンポーネント

「ステートレス非機能コンポーネント」とまったく同じように機能します。ライフサイクルがまだあるため、動作は同じになります。コンポーネントに追加の内部stateがあるという事実は何も変更しません。


これがコンポーネントでデストラクタリングを使用するときに理解するのに役立つことを願っています。私は本当に機能的な方法が好きです、それははるかにきれいな私見です(簡単にするために+1)。

機能コンポーネントまたは非機能コンポーネントのどちらを使用する場合でも、常にdefaultPropsを使用することをお勧めします。これも有効です。 (一貫性のために+1)

defaultPropsの使用を「必要とする」非機能コンポーネントのライフサイクルに注意してください。しかし、最終的に選択は常にあなた次第です;)

26
Vadorequest

高度なユースケースを見ると、コンポーネントに不要なプロパティを追加していることになります。 labelplaceholderは渡される他のプロパティに依存しており、私の意見では、コンポーネント自体のパラメーターとしてリストされるべきではありません。

アプリケーションで<FormField />を使用しようとしていて、特定のコンポーネントの依存関係を確認する必要がある場合、他のパラメーターに基づくパラメーターを作成する理由について少し混乱するでしょう。 。 labelplaceholderを関数の本体に移動して、clearになるようにします。これらはコンポーネントの依存関係ではなく、単なる副作用です。

ここでのパフォーマンスに関する限り、どちらの方法でも大きな違いがあるかどうかはわかりません。ステートレスコンポーネントには、ステートフルコンポーネントにあるような「バッキングインスタンス」が実際にはありません。つまり、コンポーネントを追跡するメモリオブジェクトが存在しません。これは、パラメーターを渡してビューを返すだけの純粋な機能だと思います。

同じメモに.. PropTypesを追加すると、型チェックに役立ちます。

const FormField = ({
  input: { name, value, ...inputRest },
  row = false,
  meta: { touched, error, warning },
  ...others,
}) => {
  const label = capitalize(name),
  const placeholder = label,

  return (
    // logic
  );
};

FormField.propTypes = {
  input: PropTypes.shape({
    name: PropTypes.string.isRequired,
    value: PropTypes.string,
  }).isRequired,
  meta: PropTypes.shape({
    touched: PropTypes.bool.isRequired,
    error: PropTypes.bool.isRequired,
    warning: PropTypes.bool.isRequired,
  }).isRequired,
  row: PropTypes.bool.isRequired,
};
1
Taylor Jones