スタイル付きコンポーネントを使用したReactアプリでのダークモードの実装
公開: 2022-03-10最も一般的に要求されるソフトウェア機能の1つは、ダークモード(または他の人が呼ぶようにナイトモード)です。 毎日使用するアプリにダークモードが表示されます。 モバイルアプリからウェブアプリまで、ダークモードはユーザーの目を大事にしたい企業にとって不可欠になっています。
ダークモードは、UIにほとんど暗い表面を表示する補足機能です。 ほとんどの主要企業(YouTube、Twitter、Netflixなど)は、モバイルアプリとWebアプリにダークモードを採用しています。
Reactとスタイル付きコンポーネントについては詳しく説明しませんが、React、CSS、およびスタイル付きコンポーネントの基本的な知識が役立ちます。 このチュートリアルは、ダークモードを愛する人に対応することで、Webアプリケーションを強化しようとしている人に役立ちます。
この記事を書く数日前に、StackOverflowはダークモードのリリースを発表しました。これにより、ユーザーは2つのモードを切り替えることができます。
ダークモードは目の疲れを軽減し、コンピューターや携帯電話で長時間作業しているときに役立ちます。
ダークモードとは何ですか?
ダークモードは、暗い背景に明るいテキストとインターフェイス要素を表示するインターフェイスの配色です。これにより、画面が携帯電話、タブレット、およびコンピューターで少し見やすくなります。 ダークモードは、読みやすさに必要な最小限のカラーコントラスト比を維持しながら、画面から放出される光を減らします。
なぜダークモードを気にする必要があるのですか?
ダークモードは、目の疲れを軽減し、画面を現在の光の状態に調整し、夜間や暗い環境での使いやすさを提供することにより、視覚的な人間工学を強化します。
アプリにダークモードを実装する前に、その利点を見てみましょう。
バッテリーの節約
ウェブアプリやモバイルアプリのダークモードは、デバイスのバッテリー寿命を延ばすことができます。 Googleは、OLED画面のダークモードがバッテリー寿命の大きな助けになっていることを確認しました。
たとえば、50%の明るさで、YouTubeアプリのダークモードは、平らな白い背景よりも約15%多くの画面エネルギーを節約します。 画面の明るさが100%の場合、暗いインターフェイスは画面のエネルギーを60%節約します。
ダークモードは美しい
ダークモードは美しく、画面の魅力を大幅に高めることができます。
ほとんどの製品は同じような淡い白の外観を目指していますが、ダークモードは神秘的で新しい感じの別の何かを提供します。
また、ダッシュボード、写真、写真などのグラフィックコンテンツを新鮮な方法で提示する絶好の機会を提供します。
次のWebアプリでダークモードを実装する必要がある理由がわかったので、このチュートリアルの定義リソースであるスタイル付きコンポーネントについて詳しく見ていきましょう。
ダークモードは、暗い背景に明るいテキストとインターフェイス要素を表示するインターフェイスの配色です。これにより、携帯電話、タブレット、およびコンピューターで見やすくなります。
「「
スタイル付きコンポーネントとは何ですか?
この記事全体を通して、styled-componentsライブラリを頻繁に使用します。 最新のWebアプリをスタイリングする方法は常にたくさんあります。 ドキュメントレベルでスタイリングする従来の方法があります。これには、 index.css
ファイルを作成し、それをHTMLにリンクするか、HTMLファイル内でスタイリングすることが含まれます。
CSS-in-JSの導入以来、最近Webアプリのスタイル設定方法が大幅に変更されました。
CSS-in-JSは、JavaScriptを使用してCSSを構成するパターンを指します。 タグ付きのテンプレートリテラルを利用して、JavaScriptファイルのコンポーネントのスタイルを設定します。
CSS-in-JSの詳細については、このテーマに関するAnnaMonusの記事をご覧ください。
styled-componentsはCSS-in-JSライブラリであり、メディアクエリ、疑似セレクター、ネストなど、CSSのすべての機能を使用できます。
なぜスタイルコンポーネント?
styled-componentsは、次の理由で作成されました。
- クラス名地獄はありません
頭をかいて要素のクラス名を見つける代わりに、styled-componentsはスタイルに固有のクラス名を生成します。 スペルミスや意味のないクラス名の使用について心配する必要はありません。 - 小道具の使用
styled-componentsを使用すると、Reactで一般的に使用されるprops
パラメーターを使用してスタイリングプロパティを拡張できます。したがって、アプリケーションの状態を介してコンポーネントの感触に動的に影響を与えます。 - Sass構文をサポートします
スタイル付きコンポーネントを使用すると、プリプロセッサや追加のビルドツールを設定せずに、Sass構文をすぐに作成できます。 スタイル定義では、&
文字を使用して現在のコンポーネントをターゲットにし、疑似セレクターを使用して、ネストを試すことができます。 - テーマ
styled-componentsは、ThemeProvider
ラッパーコンポーネントをエクスポートすることにより、完全なテーマ設定をサポートしています。 このコンポーネントは、ContextAPIを介してそれ自体の中にあるすべてのReactコンポーネントにテーマを提供します。 レンダリングツリーでは、複数のレベルの深さの場合でも、すべてのスタイル付きコンポーネントが提供されたテーマにアクセスできます。 このチュートリアルを続けるにつれ、スタイル付きコンポーネントのテーマ機能について詳しく見ていきます。
スタイル付きコンポーネントのその他の利点については、KrisGuzmanの記事をご覧ください。
ダークモードの実装
この記事では、シンプルなYouTubeのようなWebページにダークモードを実装します。
フォローするには、 starter
ブランチから元のリポジトリのクローンを作成してください。
セットアップ
package.json
ファイルにすべての依存関係をインストールしましょう。 ターミナルから、次のコマンドを実行します。
npm install
インストールが成功したら、 npm start
を実行します。 ダークモードが実装されていないWebページは次のようになります。
styled-components
をインストールするには、ターミナルでnpm install styled-components
を実行します。
実装
ダークモードを実装するには、4つの異なるコンポーネントを作成する必要があります。
-
Theme
これには、明るいテーマと暗いテーマの色のプロパティが含まれています。 -
GlobalStyles
これには、ドキュメント全体のグローバルスタイルが含まれます。 -
Toggler
これは、機能を切り替えるボタン要素を保持します。 -
useDarkMode
このカスタムフックは、テーマの変更とlocalStorageでのテーマの永続性の背後にあるロジックを処理します。
テーマコンポーネント
src
フォルダーでは、 components
フォルダーにコンポーネントが表示されます。 Themes.js
ファイルを作成し、次のコードを追加します。
export const lightTheme = { body: '#FFF', text: '#363537', toggleBorder: '#FFF', background: '#363537', } export const darkTheme = { body: '#363537', text: '#FAFAFA', toggleBorder: '#6B8096', background: '#999', }
ここでは、異なる色変数を使用してdarkTheme
lightTheme
を定義およびエクスポートしました。 自由に実験して、自分に合うように変数をカスタマイズしてください。
globalStylesコンポーネント
components
フォルダに残り、 globalStyles.js
ファイルを作成し、次のコードを追加します。
import { createGlobalStyle} from "styled-components" export const GlobalStyles = createGlobalStyle` body { background: ${({ theme }) => theme.body}; color: ${({ theme }) => theme.text}; font-family: Tahoma, Helvetica, Arial, Roboto, sans-serif; transition: all 0.50s linear; } `
styled-componentsからcreateGlobalStyle
をインポートしました。 createGlobalStyle
メソッドは、廃止されたinjectGlobalメソッドをstyled-componentsバージョン3から置き換えます。このメソッドはReactコンポーネントを生成し、コンポーネントツリーに追加すると、グローバルスタイルをドキュメント(この場合はApp.js
)に挿入します。
GlobalStyle
コンポーネントを定義し、テーマオブジェクトの値にbackground
とcolor
プロパティを割り当てました。 したがって、トグルを切り替えるたびに、 ThemeProvider
に渡すダークテーマまたはライトテーマオブジェクトに応じて値が変化します(後で作成されます)。
0.50s
の遷移プロパティにより、この変更がもう少しスムーズに発生するため、前後に切り替えると、変更が発生することがわかります。
テーマ切り替え機能の作成
テーマ切り替え機能を実装するには、数行のコードを追加するだけです。 App.js
ファイルに、次のコードを追加します(強調表示されたコードが追加する必要があることに注意してください)。
import React, { useState, useEffect } from "react";
import {ThemeProvider} from "styled-components"; import { GlobalStyles } from "./components/Globalstyle"; import { lightTheme, darkTheme } from "./components/Themes"
import "./App.css"; import dummyData from "./data"; import CardList from "./components/CardList"; const App = () => { const [videos, setVideos] = useState([]);
const [theme, setTheme] = useState('light'); const themeToggler = () => { theme === 'light' ? setTheme('dark') : setTheme('light') }
useEffect(() => { const timer = setTimeout(() => { setVideos(dummyData); }, 1000); return () => clearTimeout(timer); }, []); return (
<ThemeProvider theme={theme === 'light' ? lightTheme : darkTheme}> <> <GlobalStyles/>
<div className="App">
<button onClick={themeToggler}>Switch Theme</button>
{ videos.map((list, index) => { return ( <section key={index}> <h2 className="section-title">{list.section}</h2> <CardList list={list} /> <hr /> </section> ); })} </div>
</> </ThemeProvider>
); }; export default App;
強調表示されているコードは、 App.js
に新しく追加されたコードです。 styled-components
からThemeProvider
をインポートしました。 ThemeProvider
は、テーマのサポートを提供するstyled-componentsライブラリのヘルパーコンポーネントです。 このヘルパーコンポーネントは、Context APIを介して、それ自体の下にあるすべてのReactコンポーネントにテーマを挿入します。
レンダリングツリーでは、複数のレベルの深さの場合でも、すべてのスタイル付きコンポーネントが提供されたテーマにアクセスできます。 「テーマ」のセクションをご覧ください。
次に、 GlobalStyle
ラッパーを./components/Globalstyle
からインポートします。 最後に、上から、lightThemeオブジェクトとdarkTheme
オブジェクトの両方をlightTheme
から./components/Themes
します。
トグルメソッドを作成するには、テーマの初期カラー値を保持する状態が必要です。 そこで、 useState
フックを使用して、 theme
の状態を作成し、初期状態をlight
に設定します。
さて、トグル機能について。
themeToggler
メソッドは、三項演算子を使用してtheme
の状態をチェックし、条件の値に基づいて暗いまたは明るいを切り替えます。
スタイル付きコンポーネントのヘルパーコンポーネントであるThemeProvider
は、 return
ステートメント内のすべてをラップし、その下にコンポーネントを挿入します。 GlobalStyles
はグローバルスタイルをコンポーネントに挿入することを忘れないでください。 したがって、 ThemeProvider
ラッパーコンポーネント内で呼び出されます。
最後に、 themeToggler
メソッドを割り当てるonClick
イベントを含むボタンを作成しました。
これまでの結果を見てみましょう。
App.js
ファイルをリファクタリングする必要があります。 そのコードの多くはDRYではありません。 (DRYは「自分を繰り返さない」の略で、繰り返しを減らすことを目的としたソフトウェア開発の基本原則です。)すべてのロジックはApp.js
にあるようです。 わかりやすくするために、ロジックを分離することをお勧めします。 そこで、トグル機能を処理するコンポーネントを作成します。
コンポーネントの切り替え
引き続きcomponents
フォルダー内に、 Toggler.js
ファイルを作成し、それに次のコードを追加します。
import React from 'react' import { func, string } from 'prop-types'; import styled from "styled-components" const Button = styled.button` background: ${({ theme }) => theme.background}; border: 2px solid ${({ theme }) => theme.toggleBorder}; color: ${({ theme }) => theme.text}; border-radius: 30px; cursor: pointer; font-size:0.8rem; padding: 0.6rem; } \`; const Toggle = ({theme, toggleTheme }) => { return ( <Button onClick={toggleTheme} > Switch Theme </Button> ); }; Toggle.propTypes = { theme: string.isRequired, toggleTheme: func.isRequired, } export default Toggle;
物事をすっきりさせるために、styled-componentsのstyled
関数を使用して、 Toggle
コンポーネントのトグルボタンのスタイルを設定しました。
これは純粋にプレゼンテーション用です。 必要に応じてボタンのスタイルを設定できます。
Toggle
コンポーネント内で、2つの小道具を渡します。
-
theme
は現在のテーマ(明るいまたは暗い)を提供します。 -
toggleTheme
関数は、テーマを切り替えるために使用されます。
次に、 Button
コンポーネントを返し、 onClick
イベントにtoggleTheme
関数を割り当てます。
最後に、 propTypes
を使用して型を定義し、 theme
がstring
でisRequired
であることを確認し、 toggleTheme
がfunc
でisRequired
であることを確認します。
カスタムフックの使用( useDarkMode
)
アプリケーションを構築する場合、スケーラビリティが最も重要です。つまり、ビジネスロジックは再利用可能である必要があります。これにより、さまざまな場所で、さらにはさまざまなプロジェクトで使用できるようになります。
そのため、切り替え機能を別のコンポーネントに移動すると便利です。 そのために、独自のカスタムフックを作成します。
components
フォルダーにuseDarkMode.js
という名前の新しいファイルを作成し、いくつかの調整を加えて、ロジックをこのファイルに移動しましょう。 次のコードをファイルに追加します。
import { useEffect, useState } from 'react'; export const useDarkMode = () => { const [theme, setTheme] = useState('light'); const setMode = mode => { window.localStorage.setItem('theme', mode) setTheme(mode) }; const themeToggler = () => { theme === 'light' ? setMode('dark') : setMode('light') }; useEffect(() => { const localTheme = window.localStorage.getItem('theme'); localTheme && setTheme(localTheme) }, []); return [theme, themeToggler] };
ここにいくつか追加しました。
-
setMode
localStorage
を使用して、ブラウザーのセッション間で永続化します。 したがって、ユーザーがダークテーマまたはライトテーマを選択した場合、次にアプリにアクセスしたとき、またはページをリロードしたときに、それが表示されます。 したがって、この関数は状態を設定し、theme
をlocalStorage
に渡します。 -
themeToggler
この関数は、三項演算子を使用してテーマの状態をチェックし、状態の真偽に基づいて暗いまたは明るいを切り替えます。 -
useEffect
コンポーネントの取り付けをチェックするためにuseEffect
フックを実装しました。 ユーザーが以前にテーマを選択した場合は、それをsetTheme
関数に渡します。 最後に、選択したtheme
とモードを切り替えるthemeToggler
関数を含むtheme
を返します。
ダークモードコンポーネントが滑らかに見えることに同意していただけると思います。
最後の仕上げとして、 App.js
に向かいましょう。
import React, { useState, useEffect } from "react"; import {ThemeProvider} from "styled-components";
import {useDarkMode} from "./components/useDarkMode"
import { GlobalStyles } from "./components/Globalstyle"; import { lightTheme, darkTheme } from "./components/Themes" import Toggle from "./components/Toggler" import "./App.css"; import dummyData from "./data"; import CardList from "./components/CardList"; const App = () => { const [videos, setVideos] = useState([]);
const [theme, themeToggler] = useDarkMode(); const themeMode = theme === 'light' ? lightTheme : darkTheme;
useEffect(() => { const timer = setTimeout(() => { setVideos(dummyData); }, 1000); return () => clearTimeout(timer); }, []); return (
<ThemeProvider theme={themeMode}>
<> <GlobalStyles/> <div className="App">
<Toggle theme={theme} toggleTheme={themeToggler} />
{ videos.map((list, index) => { return ( <section key={index}> <h2 className="section-title">{list.section}</h2> <CardList list={list} /> <hr /> </section> ); })} </div> </> </ThemeProvider> ); }; export default App;
強調表示されたコードがApp.js
に新しく追加されます。
まず、カスタムフックをインポートし、 theme
とthemeToggler
の小道具を分解して、 useDarkMode
関数で設定します。
useDarkMode
メソッドは、最初はApp.js
にあったtheme
の状態を置き換えることに注意してください。
当時のtheme
モードの状態に基づいて明るいテーマまたは暗いテーマをレンダリングするthemeMode
変数を宣言します。
これで、 ThemeProvider
ラッパーコンポーネントに、最近作成したthemeMode
変数がtheme
プロップに割り当てられます。
最後に、通常のボタンの代わりに、 Toggle
コンポーネントを渡します。
Toggle
コンポーネントでは、ボタンを定義してスタイルを設定し、 theme
とtoggleTheme
の両方を小道具としてそれらに渡したことを思い出してください。 したがって、私たちがしなければならないのは、これらの小道具を適切にToggle
コンポーネントに渡すことです。これは、 App.js
のボタンとして機能します。
はい! ダークモードが設定されており、ページが更新されたり、新しいタブにアクセスしたりしても色が変わらず、持続します。
実際の結果を見てみましょう。
ほとんどすべてがうまく機能しますが、私たちの経験を素晴らしいものにするために私たちができる小さなことが1つあります。 ダークテーマに切り替えてから、ページをリロードします。 ボタンの青色が灰色の前に少しの間読み込まれるのがわかりますか? これは、 useState
フックが最初にlight
テーマを開始するために発生します。 その後、 useEffect
が実行され、 localStorage
がチェックされ、 theme
がdark
に設定されます。 カスタムフックuseDarkMode.js
にジャンプして、小さなコードを追加しましょう。
import { useEffect, useState } from 'react'; export const useDarkMode = () => { const [theme, setTheme] = useState('light');
const [mountedComponent, setMountedComponent] = useState(false)
const setMode = mode => { window.localStorage.setItem('theme', mode) setTheme(mode) }; const themeToggler = () => { theme === 'light' ? setMode('dark') : setMode('light') }; useEffect(() => { const localTheme = window.localStorage.getItem('theme'); localTheme ? setTheme(localTheme) : setMode('light')
setMountedComponent(true)
}, []); return [theme, themeToggler,
}, []); return [theme, themeToggler,
mountedComponent
]
};
強調表示されたコードは、 useDarkMode.js
に追加された唯一のコードです。 mountedComponent
という名前の別の状態を作成し、 useState
フックを使用してデフォルト値をfalse
に設定しました。 次に、 useEffect
フック内で、 setMountedComponent
を使用してmountedComponent
状態をtrue
に設定します。 最後に、 return
配列に、 mountedComponent
状態を含めます。
最後に、 App.js
にコードを少し追加して、すべてが機能するようにします。
import React, { useState, useEffect } from "react"; import {ThemeProvider} from "styled-components"; import {useDarkMode} from "./components/useDarkMode" import { GlobalStyles } from "./components/Globalstyle"; import { lightTheme, darkTheme } from "./components/Themes" import Toggle from "./components/Toggler" import "./App.css"; import dummyData from "./data"; import CardList from "./components/CardList"; const App = () => { const [videos, setVideos] = useState([]);
const [theme, themeToggler, mountedComponent] = useDarkMode();
const themeMode = theme === 'light' ? lightTheme : darkTheme; useEffect(() => { const timer = setTimeout(() => { setVideos(dummyData); }, 1000); return () => clearTimeout(timer); }, []);
if(!mountedComponent) return <div/>
return ( <ThemeProvider theme={themeMode}> <> <GlobalStyles/> <div className="App"> <Toggle theme={theme} toggleTheme={themeToggler} /> { videos.map((list, index) => { return ( <section key={index}> <h2 className="section-title">{list.section}</h2> <CardList list={list} /> <hr /> </section> ); })} </div> </> </ThemeProvider> ); }; export default App;
useDarkMode
フックにmountedComponent
状態を追加し、コンポーネントがマウントされているかどうかを確認しました。これは、 useEffect
フックで発生するためです。 まだ発生していない場合は、空のdiv
をレンダリングします。
ダークモードのWebページの結果を見てみましょう。
これで、ダークモードでページをリロードしても、ボタンの色が変わらないことがわかります。
結論
ダークモードはますますユーザーの好みになりつつあり、スタイル付きコンポーネントでThemeProvider
テーマラッパーを使用すると、ReactWebアプリでの実装がはるかに簡単になります。 ダークモードを実装するときに、スタイル付きコンポーネントを試してみてください。 ボタンの代わりにアイコンを追加できます。
以下のコメントセクションで、スタイル付きコンポーネントのテーマ機能に関するフィードバックと経験を共有してください。 あなたが思いついたものを見てみたいです!
この記事のサポートリポジトリはGitHubで入手できます。 また、CodeSandboxでチェックしてください。
参考文献
- 「ドキュメント」、スタイル付きコンポーネント
- 「スタイル付きコンポーネントを使用してアプリのダークモードを作成する」、Tom Nolan、Medium