開発者ドキュメント

Reactでカスタムトグルスイッチを構築する方法

序章

Webアプリケーションの構築には、通常、ユーザーとの対話のための準備が含まれます。 ユーザーとの対話を準備する重要な方法の1つは、フォームを使用することです。 ユーザーからさまざまな種類の入力を受け取るために、さまざまなフォームコンポーネントが存在します。 たとえば、パスワードコンポーネントは、ユーザーから機密情報を取得し、それが表示されないようにマスクします。

ほとんどの場合、ユーザーから取得する必要のある情報は boolean-like です。たとえば、yesまたはnotrueまたは false enableまたはdisableonまたはoffなど。 従来、チェックボックスフォームコンポーネントは、これらの種類の入力を取得するために使用されます。 ただし、最近のインターフェイスデザインでは、アクセシビリティの問題がいくつかありますが、チェックボックスの代わりにトグルスイッチが一般的に使用されています。

このチュートリアルでは、Reactを使用してカスタムトグルスイッチコンポーネントを構築する方法を説明します。 チュートリアルの最後に、カスタムトグルスイッチコンポーネントを使用するデモReactアプリがあります。

これは、このチュートリアルで構築する最終的なアプリケーションのデモです。

前提条件

始める前に、次のものが必要です。

ステップ1—はじめに

開始するには、で新しいReactアプリケーションを作成します npxcreate-react-app. アプリケーションには任意の名前を付けることができますが、このチュートリアルではreact-toggle-switchを使用します。

  1. npx create-react-app react-toggle-switch

次に、アプリケーションに必要な依存関係をインストールします。 ターミナルウィンドウを使用して、プロジェクトディレクトリに移動します。

  1. cd react-toggle-switch

次のコマンドを実行して、必要な依存関係をインストールします。

  1. npm install bootstrap@4.5.0 lodash@4.17.15 prop-types@15.7.2 classnames@2.2.6 node-sass@4.14.1

注:のバージョンを確認してください node-sass インストールするインストールは、最小限のサポートのクイックガイドを参照することにより、ご使用の環境と互換性があります。

インストールしました bootstrap デフォルトのスタイルが必要になるため、アプリケーションの依存関係としてパッケージ化します。 アプリケーションにBootstrapを含めるには、 src/index.js ファイルを作成し、次の行を他のすべての前に追加します import 声明:

src / index.js
import "bootstrap/dist/css/bootstrap.min.css";

次のコマンドを実行してアプリケーションを起動します。 npm:

  1. npm start

アプリケーションを起動すると、開発を開始できます。 ライブリロード機能を備えたブラウザタブが開いていることに注意してください。 ライブリロードは、開発中にアプリケーションへの変更と同期し続けます。

この時点で、アプリケーションビューは次のスクリーンショットのようになります。

次に、トグルコンポーネントを作成します。

ステップ2—作成 ToggleSwitch 成分

コンポーネントを構築する前に、という名前の新しいディレクトリを作成します componentssrc プロジェクトのディレクトリ。

  1. mkdir -p src/components

次に、という名前の別の新しいディレクトリを作成します ToggleSwitchcomponents ディレクトリ。

  1. mkdir -p src/components/ToggleSwitch

内部に2つの新しいファイルを作成します src/components/ToggleSwitch、すなわち: index.jsindex.scss. を作成して開きます index.js お気に入りのテキストエディタでファイル:

  1. nano src/components/ToggleSwitch/index.js

次のコンテンツをに追加します src/components/ToggleSwitch/index.js ファイル:

src / components / ToggleSwitch / index.js
import PropTypes from 'prop-types';
import classnames from 'classnames';
import isString from 'lodash/isString';
import React, { Component } from 'react';
import isBoolean from 'lodash/isBoolean';
import isFunction from 'lodash/isFunction';
import './index.scss';

class ToggleSwitch extends Component {}

ToggleSwitch.propTypes = {
  theme: PropTypes.string,
  enabled: PropTypes.oneOfType([
    PropTypes.bool,
    PropTypes.func
  ]),
  onStateChanged: PropTypes.func
}

export default ToggleSwitch;

このコードスニペットでは、 ToggleSwitch コンポーネントと追加されたタイプは、その小道具のいくつかをチェックします。

ToggleSwitch状態の初期化

次のコードスニペットでは、の状態を初期化します。 ToggleSwitch コンポーネントを作成し、トグルスイッチの状態を取得するためのいくつかのコンポーネントメソッドを定義します。

src / components / ToggleSwitch / index.js
// ...

class ToggleSwitch extends Component {
  state = { enabled: this.enabledFromProps() }

  isEnabled = () => this.state.enabled

  enabledFromProps() {
    let { enabled } = this.props;

    // If enabled is a function, invoke the function
    enabled = isFunction(enabled) ? enabled() : enabled;

    // Return enabled if it is a boolean, otherwise false
    return isBoolean(enabled) && enabled;
  }
}

// ...

ここでは、 enabledFromProps() メソッドは解決します enabled 渡されて返される小道具 boolean レンダリング時にスイッチを有効にする必要があるかどうかを示します。 の場合 enabled 小道具は boolean、ブール値を返します。 それが function、戻り値が boolean. それ以外の場合は、 false.

からの戻り値を使用したことに注意してください enabledFromProps() イニシャルを設定するには enabled 州。 また、 isEnabled() 現在を取得する方法 enabled 州。

ToggleSwitchの切り替え

先に進んで、クリックされたときにスイッチを切り替えるメソッドを追加しましょう。 次のコードをファイルに追加します。

src / components / ToggleSwitch / index.js
// ...

class ToggleSwitch extends Component {

  // ...other class members here

  toggleSwitch = evt => {
    evt.persist();
    evt.preventDefault();

    const { onClick, onStateChanged } = this.props;

    this.setState({ enabled: !this.state.enabled }, () => {
      const state = this.state;

      // Augument the event object with SWITCH_STATE
      const switchEvent = Object.assign(evt, { SWITCH_STATE: state });

      // Execute the callback functions
      isFunction(onClick) && onClick(switchEvent);
      isFunction(onStateChanged) && onStateChanged(state);
    });
  }
}

// ...

このメソッドはとしてトリガーされるため、 click イベントリスナー、あなたはそれを宣言しました evt パラメータ。 まず、このメソッドは現在を切り替えます enabled 論理を使用した状態 NOT (!)演算子。 状態が更新されると、に渡されるコールバック関数がトリガーされます。 onClickonStateChanged 小道具。

以来注意してください onClick 最初の引数としてイベントが必要な場合は、イベントを追加で拡張します SWITCH_STATE 新しい状態オブジェクトを含むプロパティ。 しかし onStateChanged コールバックは、新しい状態オブジェクトで呼び出されます。

ToggleSwitchのレンダリング

最後に、実装しましょう render() の方法 ToggleSwitch 成分。 次のコードをファイルに追加します。

src / components / ToggleSwitch / index.js
// ...

class ToggleSwitch extends Component {

  // ...other class members here

  render() {
    const { enabled } = this.state;

    // Isolate special props and store the remaining as restProps
    const { enabled: _enabled, theme, onClick, className, onStateChanged, ...restProps } = this.props;

    // Use default as a fallback theme if valid theme is not passed
    const switchTheme = (theme && isString(theme)) ? theme : 'default';

    const switchClasses = classnames(
      `switch switch--${switchTheme}`,
      className
    )

    const togglerClasses = classnames(
      'switch-toggle',
      `switch-toggle--${enabled ? 'on' : 'off'}`
    )

    return (
      <div className={switchClasses} onClick={this.toggleSwitch} {...restProps}>
        <div className={togglerClasses}></div>
      </div>
    )
  }
}

// ...

これでたくさんのことが起こっています render() メソッドなので、少し分解してみましょう。

  1. まず、 enabled 状態はコンポーネントの状態から分解されます。
  2. 次に、コンポーネントの小道具を分解し、抽出します restProps それはスイッチに渡されます。 これにより、コンポーネントの特別な小道具を傍受して分離することができます。
  3. 次に、 classnames を使用して、スイッチと内部トグルのクラスを作成します。 theme そしてその enabled コンポーネントの状態。
  4. 最後に、適切な小道具とクラスを使用してDOM要素をレンダリングします。 渡したことに注意してください this.toggleSwitch として click スイッチのイベントリスナー。

ファイルを保存して閉じます。

これで、 ToggleSwitch.

ステップ3—スタイリング ToggleSwitch

今、あなたは ToggleSwitch コンポーネントとその必要な機能については、先に進んでそのスタイルを書くことができます。

を開きます index.scss お気に入りのテキストエディタでファイル:

  1. nano src/components/ToggleSwitch/index.scss

次のコードスニペットをファイルに追加します。

src / components / ToggleSwitch / index.scss
// DEFAULT COLOR VARIABLES

$ball-color: #ffffff;
$active-color: #62c28e;
$inactive-color: #cccccc;

// DEFAULT SIZING VARIABLES

$switch-size: 32px;
$ball-spacing: 2px;
$stretch-factor: 1.625;

// DEFAULT CLASS VARIABLE

$switch-class: 'switch-toggle';


/* SWITCH MIXIN */

@mixin switch($size: $switch-size, $spacing: $ball-spacing, $stretch: $stretch-factor, $color: $active-color, $class: $switch-class) {}

ここでは、いくつかのデフォルト変数を定義し、 switch 混入します。 次のセクションでは、ミックスインを実装しますが、最初に、 switch 混入します:

スイッチミックスインの実装

これがの実装です switch 混入します:

src / components / ToggleSwitch / index.scss
// ...

@mixin switch($size: $switch-size, $spacing: $ball-spacing, $stretch: $stretch-factor, $color: $active-color, $class: $switch-class) {

  // SELECTOR VARIABLES

  $self: '.' + $class;
  $on: #{$self}--on;
  $off: #{$self}--off;

  // SWITCH VARIABLES

  $active-color: $color;
  $switch-size: $size;
  $ball-spacing: $spacing;
  $stretch-factor: $stretch;
  $ball-size: $switch-size - ($ball-spacing * 2);
  $ball-slide-size: ($switch-size * ($stretch-factor - 1) + $ball-spacing);

  // SWITCH STYLES

  height: $switch-size;
  width: $switch-size * $stretch-factor;
  cursor: pointer !important;
  user-select: none !important;
  position: relative !important;
  display: inline-block;

  &#{$on},
  &#{$off} {
    &::before,
    &::after {
      content: '';
      left: 0;
      position: absolute !important;
    }

    &::before {
      height: inherit;
      width: inherit;
      border-radius: $switch-size / 2;
      will-change: background;
      transition: background .4s .3s ease-out;
    }

    &::after {
      top: $ball-spacing;
      height: $ball-size;
      width: $ball-size;
      border-radius: $ball-size / 2;
      background: $ball-color !important;
      will-change: transform;
      transition: transform .4s ease-out;
    }
  }

  &#{$on} {
    &::before {
      background: $active-color !important;
    }
    &::after {
      transform: translateX($ball-slide-size);
    }
  }

  &#{$off} {
    &::before {
      background: $inactive-color !important;
    }
    &::after {
      transform: translateX($ball-spacing);
    }
  }

}

このミックスインでは、ミックスインに渡されたパラメーターに基づいていくつかの変数を設定することから始めます。 次に、スタイルを作成します。 を使用していることに注意してください ::after::before スイッチのコンポーネントを動的に作成するための疑似要素。 ::before スイッチコンテナを作成します ::after 円形のボールを作成します。

また、基本クラスから状態クラスを構築し、それらを変数に割り当てる方法にも注目してください。 The $on 変数は有効な状態のセレクターにマップされますが、 $off 変数は、無効状態のセレクターにマップされます。

また、基本クラス(.switch-toggle)状態クラスと一緒に使用する必要があります(.switch-toggle--on また .switch-toggle--off)利用可能なスタイルの場合。 したがって、あなたは &#{$on}&#{$off} セレクター。

テーマスイッチの作成

今、あなたはあなたを持っています switch ミックスインでは、トグルスイッチのテーマスタイルを引き続き作成します。 2つのテーマを作成します。 defaultgraphite-small.

次のコードスニペットをに追加します src/components/ToggleSwitch/index.scss ファイル:

src / components / ToggleSwitch / index.scss
// ...

@function get-switch-class($selector) {

  // First parse the selector using `selector-parse`
  // Extract the first selector in the first list using `nth` twice
  // Extract the first simple selector using `simple-selectors` and `nth`
  // Extract the class name using `str-slice`

  @return str-slice(nth(simple-selectors(nth(nth(selector-parse($selector), 1), 1)), 1), 2);

}

.switch {
  $self: &;
  $toggle: #{$self}-toggle;
  $class: get-switch-class($toggle);

  // default theme
  &#{$self}--default > #{$toggle} {

    // Always pass the $class to the mixin
    @include switch($class: $class);

  }

  // graphite-small theme
  &#{$self}--graphite-small > #{$toggle} {

    // A smaller switch with a `gray` active color
    // Always pass the $class to the mixin
    @include switch($color: gray, $size: 20px, $class: $class);

  }
}

ここでは、最初にという名前のSass関数を作成します get-switch-class それはかかります $selector パラメータとして。 それは実行されます $selector Sass関数のチェーンを介して、ファーストクラス名を抽出しようとします。 たとえば、次を受け取った場合:

次に、のスタイルを定義します .switch クラス。 トグルクラスを動的に設定します .switch-toggle そしてそれをに割り当てます $toggle 変数。 から返されたクラス名を割り当てることに注意してください get-switch-class() への関数呼び出し $class 変数。 最後に、 switch テーマクラスを作成するために必要なパラメータとミックスインします。

テーマスイッチのセレクターの構造は次のようになっていることに注意してください。 &#{$self}--default > #{$toggle}デフォルトのテーマを例として使用)。 すべてをまとめると、これは、スタイルを適用するために、要素の階層が次のようになる必要があることを意味します。

<!-- Use the default theme: switch--default  -->
<element class="switch switch--default">

  <!-- The switch is in enabled state: switch-toggle--on -->
  <element class="switch-toggle switch-toggle--on"></element>

</element>

これは、トグルスイッチのテーマがどのように見えるかを示すデモです。

ステップ4—サンプルアプリを構築する

今、あなたは ToggleSwitch コンポーネントを必要なスタイルで反応させて、チュートリアルの最初に見たサンプルアプリの作成を始めましょう。

を変更します src/App.js 次のコードスニペットのように見えるファイル:

src / App.js
import classnames from 'classnames';
import snakeCase from 'lodash/snakeCase';
import React, { Component } from 'react';
import Switch from './components/ToggleSwitch';
import './App.css';

// List of activities that can trigger notifications
const ACTIVITIES = [
  'News Feeds', 'Likes and Comments', 'Live Stream', 'Upcoming Events',
  'Friend Requests', 'Nearby Friends', 'Birthdays', 'Account Sign-In'
];

class App extends Component {

  // Initialize app state, all activities are enabled by default
  state = { enabled: false, only: ACTIVITIES.map(snakeCase) }

  toggleNotifications = ({ enabled }) => {
    const { only } = this.state;
    this.setState({ enabled, only: enabled ? only : ACTIVITIES.map(snakeCase) });
  }

  render() {
    const { enabled } = this.state;

    const headingClasses = classnames(
      'font-weight-light h2 mb-0 pl-4',
      enabled ? 'text-dark' : 'text-secondary'
    );

    return (
      <div className="App position-absolute text-left d-flex justify-content-center align-items-start pt-5 h-100 w-100">
        <div className="d-flex flex-wrap mt-5" style={{width: 600}}>

          <div className="d-flex p-4 border rounded align-items-center w-100">
            <Switch theme="default"
              className="d-flex"
              enabled={enabled}
              onStateChanged={this.toggleNotifications}
            />

            <span className={headingClasses}>Notifications</span>
          </div>

          {/* ... Notification options here ... */}

        </div>
      </div>
    );
  }

}

export default App;

ここで初期化します ACTIVITIES 通知をトリガーできる一連のアクティビティで定数。 次に、2つのプロパティを使用してアプリの状態を初期化しました。

あなたは snakeCase Lodash のユーティリティを使用して、状態を更新する前にアクティビティをスネークケースに変換します。 したがって、 'News Feeds' になります 'news_feeds'.

次に、 toggleNotifications() 通知スイッチから受信した状態に基づいてアプリの状態を更新するメソッド。 これは、に渡されるコールバック関数として使用されます onStateChanged トグルスイッチの小道具。 アプリを有効にすると、すべてのアクティビティがデフォルトで有効になることに注意してください。 only 状態プロパティには、すべてのアクティビティが入力されます。

最後に、アプリのDOM要素をレンダリングし、通知オプション用のスロットを残しました。これはまもなく追加されます。 この時点で、アプリは次のスクリーンショットのようになります。

次に、先に進んで、このコメントがある行を探します。

{/* ... Notification options here ... */}

通知オプションを表示するには、次のコンテンツに置き換えます。

src / App.js
// ...

{ enabled && (

  <div className="w-100 mt-5">
    <div className="container-fluid px-0">

      <div className="pt-5">
        <div className="d-flex justify-content-between align-items-center">
          <span className="d-block font-weight-bold text-secondary small">Email Address</span>
          <span className="text-secondary small mb-1 d-block">
            <small>Provide a valid email address with which to receive notifications.</small>
          </span>
        </div>

        <div className="mt-2">
          <input type="text" placeholder="mail@domain.com" className="form-control" style={{ fontSize: 14 }} />
        </div>
      </div>

      <div className="pt-5 mt-4">
        <div className="d-flex justify-content-between align-items-center border-bottom pb-2">
          <span className="d-block font-weight-bold text-secondary small">Filter Notifications</span>
          <span className="text-secondary small mb-1 d-block">
            <small>Select the account activities for which to receive notifications.</small>
          </span>
        </div>

        <div className="mt-5">
          <div className="row flex-column align-content-start" style={{ maxHeight: 180 }}>
            { this.renderNotifiableActivities() }
          </div>
        </div>
      </div>

    </div>
  </div>

) }

に電話をかけたことに気付くかもしれません this.renderNotifiableActivities() アクティビティをレンダリングします。 先に進んで、このメソッドと他の残りのメソッドを実装しましょう。

次のメソッドをに追加します App 成分:

src / App.js
// ...

class App extends Component {
  // ...

  toggleActivityEnabled = activity => ({ enabled }) => {
    let { only } = this.state;

    if (enabled && !only.includes(activity)) {
      only.push(activity);
      return this.setState({ only });
    }

    if (!enabled && only.includes(activity)) {
      only = only.filter(item => item !== activity);
      return this.setState({ only });
    }
  }

  renderNotifiableActivities() {
    const { only } = this.state;

    return ACTIVITIES.map((activity, index) => {
      const key = snakeCase(activity);
      const enabled = only.includes(key);

      const activityClasses = classnames(
        'small mb-0 pl-3',
        enabled ? 'text-dark' : 'text-secondary'
      );

      return (
        <div key={index} className="col-5 d-flex mb-3">
          <Switch theme="graphite-small"
            className="d-flex"
            enabled={enabled}
            onStateChanged={ this.toggleActivityEnabled(key) }
          />

          <span className={activityClasses}>{ activity }</span>
        </div>
      );
    })
  }

  // ...
}

ここでは、 renderNotifiableActivities 方法。 を使用してすべてのアクティビティを繰り返します ACTIVITIES.map() トグルスイッチでそれぞれをレンダリングします。 トグルスイッチが graphite-small テーマ。 また、 enabled にすでに存在するかどうかを確認することにより、各アクティビティの状態 only 状態変数。

最後に、 toggleActivityEnabled のコールバック関数を提供するために使用されたメソッド onStateChanged 各アクティビティのトグルスイッチの小道具。 アクティビティを引数として渡し、コールバック関数を返すことができるように、高階関数として定義しました。 アクティビティがすでに有効になっているかどうかを確認し、それに応じて状態を更新します。

これで、アプリは次のスクリーンショットのようになります。

最初のスクリーンショットに示されているように有効にするのではなく、デフォルトですべてのアクティビティを無効にしたい場合は、次の変更を加えることができます。 App 成分:

[src/App.js]
// ...

class App extends Component {

  // Initialize app state, all activities are disabled by default
  state = { enabled: false, only: [] }

  toggleNotifications = ({ enabled }) => {
    const { only } = this.state;
    this.setState({ enabled, only: enabled ? only : [] });
  }
}

このステップで、トグルスイッチの作成が完了しました。 次のステップでは、アプリケーションへのアクセシビリティを改善する方法を学びます。

ステップ5—アクセシビリティの懸念に対処する

従来のチェックボックスの代わりにアプリケーションでトグルスイッチを使用すると、特に従来のチェックボックスを好きなようにスタイル設定するのが難しいため、よりすっきりとしたインターフェイスを作成できます。

ただし、チェックボックスの代わりにトグルスイッチを使用すると、ユーザーエージェントがコンポーネントの機能を正しく解釈できない可能性があるため、アクセシビリティの問題が発生します。

トグルスイッチのアクセシビリティを改善し、ユーザーエージェントが役割を正しく理解できるようにするために、いくつかのことができます。 たとえば、次のARIA属性を使用できます。

<switch-element tabindex="0" role="switch" aria-checked="true" aria-labelledby="#label-element"></switch-element>

トグルスイッチでさらに多くのイベントをリッスンして、ユーザーがコンポーネントを操作する方法を増やすこともできます。

結論

このチュートリアルでは、さまざまなテーマをサポートする適切なスタイルでReactアプリケーション用のカスタムトグルスイッチを作成しました。 従来のチェックボックスの代わりに、アプリケーションでそれを使用する方法を検討しました。 さらに、関連するアクセシビリティの懸念と、改善を行うために何ができるかを調査しました。

このチュートリアルの完全なソースコードについては、GitHubのreact-toggle-switch-demoリポジトリを確認してください。 また、CodeSandboxでこのチュートリアルのライブデモを入手することもできます。

モバイルバージョンを終了