私は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のデストラクチュアリングを使用すると、全体を非常に簡単に記述/理解でき、魅力のように機能します。
しかし、それは良いアイデアですか?そうでない場合、どのように適切に行いますか?
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
...は処理されませんでした。なぜですか?
まあ、私はまだその特別なユースケースを回避する方法を見つけていません。 displayName
はname
プロパティに基づいている必要があるため、defaultProps
を定義するときにアクセスできません(AFAIK)。私が見る唯一の方法は、render
メソッドで直接処理することです。おそらくもっと良い方法があります。
address
プロパティにはこの問題はありません。MyComponentプロパティに基づいていないが、小道具を必要としない完全に独立した何かに依存しているためです。
「ステートレス非機能コンポーネント」とまったく同じように機能します。ライフサイクルがまだあるため、動作は同じになります。コンポーネントに追加の内部state
があるという事実は何も変更しません。
これがコンポーネントでデストラクタリングを使用するときに理解するのに役立つことを願っています。私は本当に機能的な方法が好きです、それははるかにきれいな私見です(簡単にするために+1)。
機能コンポーネントまたは非機能コンポーネントのどちらを使用する場合でも、常にdefaultProps
を使用することをお勧めします。これも有効です。 (一貫性のために+1)
defaultProps
の使用を「必要とする」非機能コンポーネントのライフサイクルに注意してください。しかし、最終的に選択は常にあなた次第です;)
高度なユースケースを見ると、コンポーネントに不要なプロパティを追加していることになります。 label
とplaceholder
は渡される他のプロパティに依存しており、私の意見では、コンポーネント自体のパラメーターとしてリストされるべきではありません。
アプリケーションで<FormField />
を使用しようとしていて、特定のコンポーネントの依存関係を確認する必要がある場合、他のパラメーターに基づくパラメーターを作成する理由について少し混乱するでしょう。 。 label
とplaceholder
を関数の本体に移動して、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,
};