iPhoneの設定アプリ内で使われているようなON/OFFスイッチをReactで作ります。
コンポーネントを作成する
はじめに、ToggleSwitchButtonという名前のコンポーネントを作ります。
import React from 'react'
const ToggleSwitchButton = ({ className, handleChange }) => (
<div className={className}>
<input id="btn-mode" type="checkbox" onChange={handleChange} ref={ref} />
<label htmlFor="btn-mode">
</label>
</div>
)
export default ToggleSwitchButton
ToggleSwitchButtonコンポーネントについては次の通りです。
ON/OFFの仕組み
ビューはlabel要素とinput(type="checkbox")要素で構成されています。
ON/OFFスイッチは、input(type="checkbox")要素を利用して作り、チェックボックスにチェックを入れる=スイッチON、チェックを外す=スイッチOFFということにします。
また、CSSの:checkedを使用すれば、チェックが入ったときのスタイルを指定できるため、JS無しでON/OFFスイッチに動きを持たせられそうです。
と言いたいところですが、そうはいきません。なぜなら、CSSでinput(type="checkbox")要素の見た目をガラッと変えることができないからです。input(type="checkbox")要素に指定できるCSSプロパティはheightやwidth、marginといったものだけなのです。
これではON/OFFスイッチの見た目に装飾することはできません。
そこでlabel要素の出番です。
label要素の存在意義
label要素はなんのために必要なのか。
それは、input(type="checkbox")要素との関係性にあります。
label要素のfor属性とinput(type="checkbox")要素のid属性の値を等しくすることで、label要素とinput(type="checkbox")要素が紐づきます。
これにより、label要素のクリック=input(type="checkbox")要素のチェックということになるのです。
label要素であれば、どんなCSSプロパティでも指定できるため、ON/OFFスイッチの見た目に装飾が可能です。
CSSでON/OFFスイッチの見た目を表現する
それでは、CSSでlabel要素をON/OFFスイッチの見た目に装飾します。
今回はCSSファイルに書かずに、コンポーネントにスタイルを持たせる方法をとります。そのほうがコンポーネント単位でスタイルを管理できるためです。
コンポーネントにスタイルを持たせるには、styled-componentsを使います。
styled-componentsを使うには、まず下記のコマンドでstyled-componentsをインストールします。
yarn add styled-components
そして、styled-componentsのimport後、以下のようにしてStyledToggleSwitchButtonコンポーネントを作成します。
尚、StyledToggleSwitchButtonコンポーネントは、ToggleSwitchButtonコンポーネントの上に定義します。
import React from 'react'
import styled from 'styled-components'
const StyledToggleSwitchButton = styled.div`
& input {
display: none;
&:checked + label {
background-color: #003366;
&::before {
left: 2em;
}
}
}
& label {
background-color: #ff9933;
border-radius: 2em;
border: 2px solid var(--text-color);
display: flex;
align-items: center;
justify-content: space-around;
height: 2em;
position: relative;
transition: .5s;
width: 3.75em;
&::before {
background: #fff;
border-radius: 100%;
content: '';
display: inline-block;
height: 1.5em;
position: absolute;
left: 0.25em;
transition: .5s ease-out;
width: 1.5em;
z-index: 2;
}
}
`
const ToggleSwitchButton = ({ className, handleChange }) => (
// 省略
)
export default ToggleSwitchButton
StyledToggleSwitchButtonコンポーネントについてですが、styled.div`...`
とすることで、div要素とその子要素に対するスタイルを``
内に定義しています。
スタイルについてですが、ON/OFFスイッチ全体の見た目はlabel要素で作りますが、ON/OFFスイッチの動く部分(●の部分)の見た目はlabel要素内のbefore要素で作ります。
更に、:checkedでlabel要素がクリックされたとき(チェックが入ったとき)に、スイッチの背景色をダーク仕様に変更し、また、●の部分がONの方向へ移動するようにします。
尚、label要素があればinput要素は表示する必要がないため、display: none;
で非表示にします。
& input {
display: none;
&:checked + label {
background-color: #003366;
&::before {
left: 2em;
}
}
}
スタイルについては以上です。
最後に、ToggleSwitchButtonコンポーネントをStyledToggleSwitchButtonコンポーネントで囲います。こうすることで、ToggleSwitchButtonコンポーネントがStyledToggleSwitchButtonコンポーネントの``
内で定義したスタイルを持つようになります。
// 省略
const ToggleSwitchButton = ({ className, handleChange }) => (
<StyledToggleSwitchButton className={className}>
<input id="btn-mode" type="checkbox" onChange={handleChange} ref={ref} />
<label htmlFor="btn-mode">
</label>
</StyledToggleSwitchButton>
)
export default ToggleSwitchButton
ON/OFFスイッチを表示する
完成したToggleSwitchButtonコンポーネントをApp.jsにimport後、Appコンポーネントのビューに追加してページにON/OFFスイッチを表示させます。
Appコンポーネントを以下のようにします。(※importの際のパスはご自身のディレクトリ構成に合わせてください。)
import React from 'react';
import ToggleSwitchButton from './components/ToggleSwitchButton'
const App = () => {
const [isToggle, setIsToggle] = useState(false)
const handleChange = useCallback(() => {
if(isToggle) {
setIsToggle(false)
}else{
setIsToggle(true)
}
}, [isToggle])
useEffect(() => {
if(isToggle) {
alert('switch on!')
}else{
alert('switch off!')
}
}, [isToggle])
return (
<div>
<ToggleSwitchButton className="toggle-switch-button" handleChange={handleChange} />
</div>
);
};
export default App
上記では、propsとしてToggleSwitchButtonコンポーネントに独自に定義したhandleChange関数を渡しています。
これにより、スイッチをON/OFFにするとhandleChange関数が実行されてスイッチがONであれば「switch on!」のアラートが、OFFであれば「switch off!」のアラートが表示されます。