スタイル付きコンポーネントを使用したReactアプリでのダークモードの実装

公開: 2022-03-10
クイックサマリー↬ライトモードは、ほとんどのWebアプリとモバイルアプリの慣例です。 ただし、最近の開発では、暗い背景に明るいテキストとインターフェイス要素を表示するダークモードが急速にユーザーの好みになりつつあることがわかりました。 この記事では、スタイル付きコンポーネントライブラリを使用し、フックなどのReact機能を活用して、シンプルなWebページのReactアプリにダークモードを効率的に実装する方法を学習します。 また、ダークモードの長所と短所、およびそれを採用する理由についても説明します。

最も一般的に要求されるソフトウェア機能の1つは、ダークモード(または他の人が呼ぶようにナイトモード)です。 毎日使用するアプリにダークモードが表示されます。 モバイルアプリからウェブアプリまで、ダークモードはユーザーの目を大事にしたい企業にとって不可欠になっています。

ダークモードは、UIにほとんど暗い表面を表示する補足機能です。 ほとんどの主要企業(YouTube、Twitter、Netflixなど)は、モバイルアプリとWebアプリにダークモードを採用しています。

Reactとスタイル付きコンポーネントについては詳しく説明しませんが、React、CSS、およびスタイル付きコンポーネントの基本的な知識が役立ちます。 このチュートリアルは、ダークモードを愛する人に対応することで、Webアプリケーションを強化しようとしている人に役立ちます。

StackOverflowがTwitterでダークモードを発表
StackOverflowがTwitterでダークモードを発表(大プレビュー)

この記事を書く数日前に、StackOverflowはダークモードのリリースを発表しました。これにより、ユーザーは2つのモードを切り替えることができます。

ダークモードは目の疲れを軽減し、コンピューターや携帯電話で長時間作業しているときに役立ちます。

ダークモードとは何ですか?

ダークモードは、暗い背景に明るいテキストとインターフェイス要素を表示するインターフェイスの配色です。これにより、画面が携帯電話、タブレット、およびコンピューターで少し見やすくなります。 ダークモードは、読みやすさに必要な最小限のカラーコントラスト比を維持しながら、画面から放出される光を減らします。

ジャンプした後もっと! 以下を読み続けてください↓

なぜダークモードを気にする必要があるのですか?

ダークモードは、目の疲れを軽減し、画面を現在の光の状態に調整し、夜間や暗い環境での使いやすさを提供することにより、視覚的な人間工学を強化します。

アプリにダークモードを実装する前に、その利点を見てみましょう。

バッテリーの節約

ウェブアプリやモバイルアプリのダークモードは、デバイスのバッテリー寿命を延ばすことができます。 Googleは、OLED画面のダークモードがバッテリー寿命の大きな助けになっていることを確認しました。

たとえば、50%の明るさで、YouTubeアプリのダークモードは、平らな白い背景よりも約15%多くの画面エネルギーを節約します。 画面の明るさが100%の場合、暗いインターフェイスは画面のエネルギーを60%節約します。

ダークモードは美しい

ダークモードは美しく、画面の魅力を大幅に高めることができます。

ほとんどの製品は同じような淡い白の外観を目指していますが、ダークモードは神秘的で新しい感じの別の何かを提供します。

また、ダッシュボード、写真、写真などのグラフィックコンテンツを新鮮な方法で提示する絶好の機会を提供します。

Twitterのダークモードとライトモード
Twitterのダークモードとライトモードの美しさ(ラージプレビュー)

次の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ページは次のようになります。

ダークモードなしで使用するWebページ
ダークモードなしで使用する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コンポーネントを定義し、テーマオブジェクトの値にbackgroundcolorプロパティを割り当てました。 したがって、トグルを切り替えるたびに、 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を使用して型を定義し、 themestringisRequiredであることを確認し、 toggleThemefuncisRequiredであることを確認します。

カスタムフックの使用( 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を使用して、ブラウザーのセッション間で永続化します。 したがって、ユーザーがダークテーマまたはライトテーマを選択した場合、次にアプリにアクセスしたとき、またはページをリロードしたときに、それが表示されます。 したがって、この関数は状態を設定し、 themelocalStorageに渡します。
  • 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に新しく追加されます。

まず、カスタムフックをインポートし、 themethemeTogglerの小道具を分解して、 useDarkMode関数で設定します。

useDarkModeメソッドは、最初はApp.jsにあったthemeの状態を置き換えることに注意してください。

当時のthemeモードの状態に基づいて明るいテーマまたは暗いテーマをレンダリングするthemeMode変数を宣言します。

これで、 ThemeProviderラッパーコンポーネントに、最近作成したthemeMode変数がthemeプロップに割り当てられます。

最後に、通常のボタンの代わりに、 Toggleコンポーネントを渡します。

Toggleコンポーネントでは、ボタンを定義してスタイルを設定し、 themetoggleThemeの両方を小道具としてそれらに渡したことを思い出してください。 したがって、私たちがしなければならないのは、これらの小道具を適切にToggleコンポーネントに渡すことです。これは、 App.jsのボタンとして機能します。

はい! ダークモードが設定されており、ページが更新されたり、新しいタブにアクセスしたりしても色が変わらず、持続します。

実際の結果を見てみましょう。

ダークモードが実装されていますが、ブラウザのリロード時にボタンの色に不具合があります。
ダークモードが実装されていますが、ブラウザのリロード時にボタンの色に不具合があります。 (大プレビュー)

ほとんどすべてがうまく機能しますが、私たちの経験を素晴らしいものにするために私たちができる小さなことが1つあります。 ダークテーマに切り替えてから、ページをリロードします。 ボタンの青色が灰色の前に少しの間読み込まれるのがわかりますか? これは、 useStateフックが最初にlightテーマを開始するために発生します。 その後、 useEffectが実行され、 localStorageがチェックされ、 themedarkに設定されます。 カスタムフック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