React Hooksの使用例

React Hooksの使用例

この記事では、React Hooks と関数コンポーネントを使用して開発された 2 つの簡単な例を紹介します。

簡単なコンポーネントの例

ボタン コンポーネントは、最も一般的に使用される最も単純な基本コンポーネントと考えることができます。コンポーネントを開発する場合、さまざまなシナリオに適用できるように、その基本的なスタイルがある程度変化することを期待します。 2 つ目のポイントは、以前プロジェクトに取り組んでいたときに関数コンポーネントを作成したのですが、この関数コンポーネントは非常に厳密に作成されており、基本的なメソッドをバインドする方法がなかったということです。つまり、すでに持っているメソッドや特性しか記述できません。 Button コンポーネントを記述したいと思います。onClick メソッドを記述しなくても、それに付属するデフォルトの基本メソッドを使用できるようにしたいと考えています。

最初の点については、異なる classNames ごとに異なる CSS を記述することで実装が簡単になります。

2 番目のポイントは達成するのが少し難しいです。 Button のすべてのデフォルト プロパティを再度記述することはできませんが、すべてのデフォルト プロパティをインポートできれば便利です。

実際、React はすでにこれを実現するのに役立っています。 React.ButtonHTMLAttributes<HTMLElement> には、デフォルトの Button 属性が含まれています。ただし、Button コンポーネントにカスタマイズされた部分がある可能性があるため、このインターフェースを直接使用することはできません。これにはTypescriptのクロスタイプを使用することができます

NativeButtonProps = MyButtonProps & React.ButtonHTMLAttributes<HTMLElement> と入力します。

さらに、他の非カスタム関数やプロパティをインポートするには、resProps を使用する必要もあります。

以下は、Button コンポーネントの具体的な実装です。

'react' から React をインポートします
'classnames' から classNames をインポートします

ButtonSize を 'large' | 'small' と入力します
タイプ ButtonType = 'primary' | 'default' | 'danger'

インターフェイス BaseButtonProps {
 クラス名?: 文字列;
 無効?: ブール値;
 サイズ?: ボタンサイズ;
 btnType?: ボタンタイプ;
 子?: React.ReactNode;
}

タイプ NativeButtonProps = BaseButtonProps & React.ButtonHTMLAttributes<HTMLElement>
const ボタン: React.FC<NativeButtonProps>= (props) => {
 定数{
 btnタイプ、
 クラス名、
 無効、
 サイズ、
 子供たち、
 //resProps は残りのすべてのプロパティを取得するために使用されます...resProps
 } = 小道具
 // btn、btn-lg、btn-primary
 const クラス = classNames('btn', className, {
 [`btn-${btnType}`]: btnType、
 [`btn-${size}`]: サイズ、
 「無効」: 無効
 })
 戻る (
 <ボタン
  className={クラス}
  無効={無効}
  {...resProps}
 >
  {子供たち}
 </ボタン>
 )
}

ボタン.defaultProps = {
 無効: false、
 btnType: 'デフォルト'
}

デフォルトボタンをエクスポート

上記の方法で、カスタム Button コンポーネントで onClick メソッドを使用できます。以下は、Button コンポーネントの使用例です。

<ボタンが無効>こんにちは</ボタン>
<Button btnType='primary' size='large' className="haha">こんにちは</Button>
<Button btnType='danger' size='small' onClick={() => alert('haha')}>テスト</Button>

表示効果は以下のとおりです。

このコードでは、classnames という新しい npm パッケージを導入しました。具体的な使い方については、GitHub Classnames を参照してください。これを使用すると、className の拡張を簡単に実装できます。簡単な使用例は次のとおりです。

classNames('foo', 'bar'); // => 'foo bar'
classNames('foo', { bar: true }); // => 'foo bar'
classNames({ 'foo-bar': true }); // => 'foo-bar'
classNames({ 'foo-bar': false }); // => ''
classNames({ foo: true }, { bar: true }); // => 'foo bar'
classNames({ foo: true, bar: true }); // => 'foo bar'

// さまざまなタイプの引数が多数あります
classNames('foo', { bar: true, duck: false }, 'baz', { quux: true }); // => 'foo bar baz quux'

// その他の偽の値は無視されます
classNames(null, false, 'bar', undefined, 0, 1, { baz: null }, ''); // => 'bar 1'

classNames を使用すると、Button にパーソナライズされた属性を簡単に追加できます。コンポーネントの HTML 出力に haha​​className があることがわかります。

<button class="btn haha​​ ​​btn-primary btn-lg">こんにちは</button>

同時に、上記のコード メソッドは、カスタム コンポーネントがデフォルトのプロパティとメソッドを使用できないという問題も解決します。

より複雑な親子コンポーネントのケース

次に、関数コンポーネントを使用してメニュー機能を完成させる方法を説明します。このメニューには、水平モードと垂直モードの 2 つの機能モードが追加されます。メニューの詳細をクリックし、この詳細を子コンポーネントとして使用します。

もちろん、メニュー機能では、親コンポーネントが子コンポーネントにデータを渡す必要はありません(子コンポーネントはメニューの詳細を参照します)。親コンポーネントのデータを子コンポーネントに渡す方法を学習および実演するために、この機能を強制的に追加します。少し余計なことですが、皆様にご理解いただければ幸いです。

まず、親コンポーネントと子コンポーネントの機能説明を紹介します。 Menu は全体的な親コンポーネント、MenuItem はそれぞれの特定の小さなメニュー、SubMenu にはクリックできるドロップダウン メニューが含まれています。

次の図は、拡張後の状態を示しています。

全体的なコード構造は次のとおりです。

<メニュー defaultIndex={'0'} onSelect={(index) => {alert(index)}} mode="vertical" defaultOpenSubMenus={['2']}>
 <メニュー項目インデックス={'0'}>
 クールなリンク
 </メニュー項目>
 <メニュー項目インデックス={'1'}>
 クールなリンク2
 </メニュー項目>
 <サブメニュー title="ドロップダウン">
 <メニュー項目インデックス={'3'}>
  ドロップダウン 1
 </メニュー項目>
 <メニュー項目インデックス={'4'}>
  ドロップダウン 2
 </メニュー項目>
 </サブメニュー>
 <メニュー項目インデックス={'2'}>
 クールなリンク3
 </メニュー項目>
</メニュー>

このコンポーネントでは、useState を使用しました。また、親コンポーネントから子コンポーネントにデータを渡すため、useContext も使用しました (親コンポーネントから子コンポーネントにデータを渡すということは、親コンポーネントのインデックス データを子コンポーネントに渡すことを意味します)。さらに、カスタム onSelect を使用して onClick 機能を実装する方法も紹介します (React ジェネリックの導入に失敗した場合や、どの React ジェネリックを導入すればよいかわからない場合は、カスタム ジェネリックを使用して状況を改善できます)。

オンセレクトの書き方

後で広大なコードの海の中で onSelect を見つけるのが困難にならないように、onSelect の簡単な記述例を示します。たとえば、Menu コンポーネントでは onSelect を使用しますが、その使用方法は onClick と同じです。

<メニュー onSelect={(index) => {alert(index)}}>

この特定のメニュー コンポーネントでは、onSelect メソッドは次のように記述できます。

タイプ SelectCallback = (selectedIndex: string) => void

インターフェースMenuProps {
 onSelect?: SelectCallback;
}

handleClick を実装するメソッドは次のように記述できます。

 const handleClick = (インデックス: 文字列) => {
 // onSelect はユニオン型であり、存在するかどうかは不明なので、判断する必要があります if (onSelect) {
  onSelect(インデックス)
 }
 }

この onSelect を子コンポーネントに渡す場合は、onSelect: handleClick を使用してバインドするだけです。 (よくわからなかったかもしれませんし、私も書き方がわかりません。後ほど全体的なコード分析があるので、一緒に読んでいただくとわかりやすいかもしれません)

反応する子供

具体的なコードを説明する前に、もう少し知識として知っておくべき点がいくつかあります。そのうちの 1 つが React.Children です。

React.Children は、this.props.children 不透明データ構造を操作するためのユーティリティ メソッドを提供します。

React.Children を使用する必要があるのはなぜですか?これは、親コンポーネントのデータを子コンポーネントに渡す場合、子コンポーネントを 2 回走査したり、さらに処理したりする必要がある場合があるためです。ただし、サブコンポーネントが存在するかどうか、また、サブコンポーネントが 1 つ、2 つ、またはそれ以上あるかどうかは保証できません。

this.props.children には 3 つの値があります。現在のコンポーネントに子ノードがない場合、undefined になります。子ノードが 1 つある場合、データ型はオブジェクトになります。子ノードが複数ある場合、データ型は配列になります。したがって、this.props.children[1]を扱うときは注意が必要です。

React は、this.props.children を処理するためのユーティリティ メソッド React.Children を提供します。 React.Children.mapを使用すると、this.props.childrenのデータ型がundefinedかobject[1]かを気にすることなく、子ノードを反復処理できます。

したがって、親子コンポーネントがある場合、子コンポーネントをさらに処理する必要がある場合は、React.Children を使用してそれらをトラバースすることができ、this.props.children の型の変更によるエラーは発生しません。

React.cloneElement

React.Children は React.cloneElement と一緒に出現することがよくあります。そのため、React.cloneElement も導入する必要があります。

複雑なコンポーネントを開発する場合、必要に応じて子コンポーネントにさまざまな機能や表示効果を追加する必要があることがよくあります。React要素自体は不変オブジェクトです。props.childrenは実際には子そのものではなく、子の記述子にすぎません。そのプロパティを変更することはできず、その内容を読み取ることしかできません。したがって、React.cloneElementを使用すると、その要素をコピーして、目的を達成するために新しいpropsを変更または追加することができます[2]。

たとえば、子要素に対してさらに処理を実行する必要がある場合もありますが、React 要素自体は不変であるため、さらに処理するには複製する必要があります。このメニュー コンポーネントでは、サブコンポーネントは MenuItem または SubMenu の 2 つのタイプのみになることを期待しています。他のタイプの場合、警告メッセージが報告されます。具体的には、コードは大まかに次のように記述できます。

if (displayName === 'メニュー項目' || displayName === 'サブメニュー') {
 // 要素をサンプルとして新しい React 要素を複製して返します。最初のパラメータは複製されたサンプルです。 return React.cloneElement(childElement, {
 インデックス: index.toString()
 })
} それ以外 {
 console.error("警告: メニューには MenuItem コンポーネントではない子があります")
}

親コンポーネントのデータを子コンポーネントに渡す方法

コンテキストは、親コンポーネントのデータを子コンポーネントに渡すために使用されます。 Context について詳しくない場合は、公式ドキュメント Context を参照してください。親コンポーネントでは、createContext を通じて Context を作成し、子コンポーネントでは、useContext を通じて Context を取得します。

インデックスデータ転送

Menu コンポーネント内の親コンポーネントと子コンポーネント間のデータ転送を実装するための主な変数は index です。

最後に、完全なコードを添付します。最初は Menu 親コンポーネントです。

React をインポートし、{useState、createContext} を 'react' から取得します。
'classnames' から classNames をインポートします
'./menuItem' から { MenuItemProps } をインポートします。

タイプ MenuMode = 'horizo​​ntal' | 'vertical'
タイプ SelectCallback = (selectedIndex: string) => void

エクスポートインターフェースMenuProps {
 defaultIndex?: string; // どのメニューサブコンポーネントが強調表示されるのに使用されますか? className?: string;
 モード?: MenuMode;
 スタイル?: React.CSSProperties;
 onSelect?: SelectCallback; // サブメニューをクリックするとコールバックがトリガーされます defaultOpenSubMenus?: string[]; 
}

// 親コンポーネントから子コンポーネントに渡されるデータ型を決定する interface IMenuContext {
 インデックス: 文字列;
 onSelect?: SelectCallback;
 モード?: MenuMode;
 defaultOpenSubMenus?: string[]; // コンテキストにデータを渡す必要がある
}

// 子コンポーネントに渡すコンテキストを作成する
// 一般的な制約。インデックスは入力する値なので、ここにデフォルトの初期値を記述します。 export const MenuContext = createContext<IMenuContext>({index: '0'})

const メニュー: React.FC<MenuProps> = (props) => {
 const { className、mode、style、children、defaultIndex、onSelect、defaultOpenSubMenus} = props
 // アクティブ状態の MenuItem は 1 つだけなので、その状態を制御するには useState を使用します。const [ currentActive, setActive ] = useState(defaultIndex)
 const クラス = classNames('menu-demo', className, {
 'menu-vertical': モード === 'vertical'、
 'menu-horizo​​ntal': モード === 'horizo​​ntal'
 })

 // menuItem をクリックした後にアクティブの変更を実装するために handleClick を定義します。const handleClick = (index: string) => {
 setActive(インデックス)
 // onSelect はユニオン型であり、存在するかどうかは不明なので、判断する必要があります if (onSelect) {
  onSelect(インデックス)
 }
 }

 // サブコンポーネントをクリックすると、onSelect 関数がトリガーされ、ハイライト表示が変更されます。const passingContext: IMenuContext = {
 // currentActive は string | undefined 型で、index は number 型なので、型 index をさらに明確にするために次の判断を行う必要があります: currentActive ? currentActive : '0',
 onSelect: handleClick, // コールバック関数、サブコンポーネントをクリックしたときにトリガーするかどうか mode: mode,
 デフォルトOpenSubMenus、
 }

 const レンダリング子 = () => {
 React.Children.map(children, (child, index) => { を返します。
  // child には多くの型が含まれています。スマートプロンプトを提供したい型を取得するには、型アサーション const childElement = child as React.FunctionComponentElement<MenuItemProps> を使用する必要があります。
  const { displayName } = 子要素の型
  if (displayName === 'メニュー項目' || displayName === 'サブメニュー') {
  // 要素をサンプルとして新しい React 要素を複製して返します。最初のパラメータは複製されたサンプルです。 return React.cloneElement(childElement, {
   インデックス: index.toString()
  })
  } それ以外 {
  console.error("警告: メニューには MenuItem コンポーネントではない子があります")
  }
 })
 }
 戻る (
 <ul className={クラス} style={スタイル}>
  <MenuContext.Provider 値 = {passedContext}>
  {レンダリング子()}
  </メニューコンテキスト.プロバイダー>
 </ul>
 )
}

メニュー.defaultProps = {
 デフォルトインデックス: '0',
 モード: '水平'、
 デフォルトのOpenSubMenus: []
}

デフォルトメニューをエクスポート

次に、MenuItem サブコンポーネント:

'react' から React をインポートします
'react' から {useContext} をインポートします。
'classnames' から classNames をインポートします
'./menu' から { MenuContext } をインポートします。

エクスポートインターフェースMenuItemProps {
 インデックス: 文字列;
 無効?: ブール値;
 クラス名?: 文字列;
 スタイル?: React.CSSProperties;
}

const メニュー項目: React.FC<MenuItemProps> = (props) => {
 const { インデックス、無効、クラス名、スタイル、子 } = プロパティ
 const コンテキスト = useContext(メニューコンテキスト)
 const classes = classNames('メニュー項目', className, {
 'is-disabled': 無効、
 // 'is-active' を強調表示する特定のロジックを実装します: context.index === index
 })
 const ハンドルクリック = () => {
 // 無効にするとonSelectは使えなくなります。indexはオプションなので存在しない可能性があります。typeofを使って判断する必要があります if (context.onSelect && !disabled && (typeof index === 'string')) {
  コンテキスト.onSelect(インデックス)
 }
 }
 戻る (
 <li className={classes} style={style} onClick={handleClick}>
  {子供たち}
 </li>
 )
}

MenuItem.displayName = 'メニュー項目'
デフォルトのメニュー項目をエクスポートする

最後に、SubMenu サブコンポーネント:

'react' から React、{useContext、FunctionComponentElement、useState } をインポートします。
'classnames' から classNames をインポートします
'./menu' から { MenuContext } をインポートします。
'./menuItem' から { MenuItemProps } をインポートします。

エクスポートインターフェース SubMenuProps {
 インデックス?: 文字列;
 タイトル: 文字列;
 クラス名?: 文字列
}

const サブメニュー: React.FC<SubMenuProps> = ({ インデックス、タイトル、子、クラス名 }) => {
 const コンテキスト = useContext(メニューコンテキスト)
 // 次に、文字列配列のいくつかのメソッドを使用します。まず、型アサーションを作成し、文字列配列型としてアサートします。const openedSubMenus = context.defaultOpenSubMenus as Array<string>
 // include を使用してインデックスがあるかどうかを判断します
 const isOpened = (index && context.mode === 'vertical') ? openedSubMenus.includes(index) : false
 const [ menuOpen, setOpen ] = useState(isOpened) // isOpened は動的な値である true または false を返します。const classes = classNames('menu-item submenu-item', className, {
 'is-active': context.index === インデックス
 })
 // ドロップダウンメニューを表示または非表示にするために使用されます const handleClick = (e: React.MouseEvent) => {
 e.preventDefault()
 setOpen(!menuOpen)
 }
 タイマー: 任意
 // トグルは開くか閉じるかを決定するために使用されます const handleMouse = (e: React.MouseEvent, toggle: boolean) => {
 タイムアウトをクリア(タイマー)
 e.preventDefault()
 タイマー = setTimeout(()=> {
  setOpen(トグル)
 }, 300)
 }
 // 三項式、垂直 const clickEvents = context.mode === 'vertical' ? {
 onClick: ハンドルクリック
 } : {}
 const hoverEvents = context.mode === 'horizo​​ntal' ? {
 onMouseEnter: (e: React.MouseEvent) => { handleMouse(e, true) },
 onMouseLeave: (e: React.MouseEvent) => { handleMouse(e, false) },
 } : {}

 // ドロップダウン メニューの内容をレンダリングするために使用されます // 2 つの値を返します。1 つ目は子、2 つ目は i で表されるインデックスです。const renderChildren = () => {
 const subMenuClasses = classNames('menu-submenu', {
  'メニューを開いた': menuOpen
 })
 // 次の関数は、サブメニューにのみ MenuItem が存在することを実装するために使用されます。
 const childrenComponent = React.Children.map(children, (child, i) => {
  const childElement = 子を FunctionComponentElement<MenuItemProps> として定義します。
  子要素のtype.displayNameが'メニュー項目'の場合{
  React.cloneElement(childElement, { を返します。
   インデックス: `${index}-${i}`
  })
  } それ以外 {
  console.error("警告: SubMenu には MenuItem コンポーネントではない子があります")
  }
 })
 戻る (
  <ul className={サブメニュークラス}>
  {子コンポーネント}
  </ul>
 )
 }
 戻る (
 // 演算子を展開し、内部に機能を追加し、外部にホバーします <li key={index} className={classes} {...hoverEvents}>
  <div className="サブメニュータイトル" {...clickEvents}>
  {タイトル}
  </div>
  {レンダリング子()}
 </li>
 )
}

サブメニュー.displayName = 'サブメニュー'
デフォルトのサブメニューをエクスポート

参考文献

  • React.Children の使用
  • React.cloneElementの使用

以上がReact Hookの使用例の詳細です。React Hookの使用についての詳細は、123WORDPRESS.COM内の他の関連記事にも注目してください!

以下もご興味があるかもしれません:
  • フックを使用して React コンポーネントを書くときに注意すべき 5 つの点
  • Reactフック入門チュートリアル
  • Reactフックとzarmコンポーネントライブラリ構成に基づいてh5フォームページを開発するためのサンプルコード
  • Reactドラッグフックを実装するための100行以上のコード
  • Reactのフックの紹介と使用方法のチュートリアル
  • React Hooksを使用してデータをリクエストしレンダリングする方法の詳細な説明
  • 反応フックの使い方の詳細な説明
  • React Hooks の実装と起源、そしてそれが解決する問題の詳細な説明
  • 反応フックのユニットテスト方法
  • Reactはフックを使用して、制御されたコンポーネントの状態バインディングを簡素化します。
  • Reactにおけるフックの一般的な使用法

<<:  Nginxはctxを使用してデータ共有とコンテキスト変更機能を実現します。

>>:  MySQL 5.7.18 バージョンのインストール パスのカスタマイズに関する詳細なチュートリアル (バイナリ パッケージのインストール)

推薦する

写真とテキストによる MySQL と sqlyog のインストール チュートリアル

1. MySQL 1.1 MySQLのインストールmysql-5.5.27-winx64 ダウンロー...

::before/:before と ::after/:after の使用に関する深い理解

パート1: 基礎1. :active や :hover などの疑似クラスとは異なり、これらはすべて疑...

HTML 特殊文字エンコーディング CSS3 コンテンツに関する簡単な説明:「私は特別なシンボルです」

プロジェクトで使用されている特殊文字とアイコンHTMLコードXML/HTML コードコンテンツをクリ...

Nginx gzip設定について

nginx がリソース圧縮を実現する原理は、ngx_http_gzip_module モジュールを介...

ウェブページを作成するために最もよく使用されるHTMLタグ

1. よく使われるHTMLタグの最適化HTML は Web 編集者にとって基本的なスキルであるべきで...

この SQL 書き込み方法では本当にインデックスが失敗するのでしょうか?

序文インターネット上には、MySQL でインデックスにヒットできないさまざまな状況をまとめた記事がよ...

MySQLのページング制限のパフォーマンス問題についての簡単な説明

MySQL ページング クエリは通常、制限を通じて実装されます。 limit は 1 つまたは 2 ...

HTML テーブル マークアップ チュートリアル (29): セルのライト境界線の色属性 BORDERCOLORLIGHT

セルでは、明るい境界線の色を個別に定義できます。 > 基本構文<TD ボーダーカラーライ...

Dockerバッチコンテナオーケストレーションの実装

導入Dockerfile ビルドの実行は、単一のコンテナの手動操作です。マイクロサービス アーキテク...

Javascript 共通高階関数の詳細

目次1. 一般的な高階関数1.1、フィルター1.2、地図1.3、減らすHigher Order fu...

シンプルなカレンダー効果を実現する js

この記事では、シンプルなカレンダー効果を実現するためのjsの具体的なコードを参考までに共有します。具...

MySQL における楽観的ロック、悲観的ロック、MVCC の包括的な分析

序文データベースの実際の使用では、データの書き込みや読み取りを同時に行わないことが必要な状況によく遭...

Linux システムを起動時に自動的にスクリプトを実行するように設定する方法の例

序文みなさんこんにちは。私は梁旭です。職場では、システムの起動後にスクリプトやサービスを自動的に開始...

CSS3 box-shadow プロパティの詳細な例

CSS3 - 影の追加(ボックスシャドウの使用) CSS3 - div またはテキストに影を追加する...

Alibaba Cloud Centos 7.5 に MySQL をインストールするチュートリアル

CentOS 7 の yum ソースには、MySQL を正常にインストールするための mysql-s...