使用样式化组件在React Apps中实现暗模式

使用样式化组件在React Apps中实现暗模式

时间:2020-7-9 作者:gykj

其中最常见的要求的软件功能是暗模式(或夜间模式,因为别人称呼它)。我们每天使用的应用程序中都会看到黑暗模式。从移动应用程序到Web应用程序,暗模式对于想要照顾用户眼睛的公司而言至关重要。

深色模式是一项补充功能,可在UI中显示大部分深色表面。大多数主要公司(例如YouTube,Twitter和Netflix)已在其移动和网络应用程序中采用了暗模式。

虽然我们不会深入研究React和样式化组件,但是对React,CSS和样式化组件的基本知识将派上用场。本教程将使那些喜欢深色模式的人受益于那些希望增强其Web应用程序的人。

StackOverflow在Twitter上宣布暗模式
StackOverflow在Twitter上宣布暗模式(大预览

在撰写本文之前的几天,StackOverflow宣布发布了暗模式,使用户有机会在两种模式之间进行切换。

深色模式可减轻眼睛疲劳,并在长时间在计算机或手机上工作时提供帮助。

什么是黑暗模式?

深色模式是任何在深色背景上显示浅色文本和界面元素的界面的配色方案,这使屏幕更易于查看手机,平板电脑和计算机。暗模式减少了屏幕发出的光,同时保持了可读性所需的最小颜色对比度。

为什么要关心黑暗模式?

暗模式通过减少眼睛疲劳,将屏幕调整为当前的光照条件并在夜间或黑暗环境中提供易用性来增强视觉人体工程学。

在我们的应用中实施暗模式之前,让我们先看看它的好处。

节电

Web和移动应用程序中的暗模式可以延长设备的电池寿命。Google已经确认 OLED屏幕的暗模式对电池寿命有很大帮助。

例如,在亮度为50%的情况下,YouTube应用中的黑暗模式比纯白色背景可节省约15%的屏幕能量。屏幕亮度为100%时,深色界面可节省60%的屏幕能量。

暗模式很漂亮

暗模式很漂亮,它可以显着增强屏幕的吸引力。

尽管大多数产品都具有类似的淡淡白色外观,但深色模式却提供了不同的感觉和神秘感。

它还为以新颖的方式呈现图形内容(例如仪表板,图片和照片)提供了绝佳的机会。

Twitter暗光模式
Twitter的暗模式优于亮模式(大预览

既然您知道了为什么要在下一个Web应用程序中实现暗模式,那么让我们深入研究样式化组件,这是本教程的定义资源。

深色模式是任何在深色背景上显示浅色文本和界面元素的界面的配色方案,这使得在手机,平板电脑和计算机上查看起来更加容易。

什么是样式组件?

在本文中,我们将经常使用样式化的组件库。一直有很多样式来设计现代Web应用程序。在文档级别有一种传统的样式化方法,包括创建index.css文件并将其链接到HTML或在HTML文件中进行样式化。

自从CSS-in-JS引入以来,Web应用程序的样式最近发生了很多变化。

CSS-in-JS是指使用JavaScript组成CSS的模式。它利用标记的模板文字来对JavaScript文件中的组件进行样式设置。

要了解有关CSS-in-JS的更多信息,请查看Anna Monus关于该主题的文章。

styled-components是一个CSS-in-JS库,可让您使用自己喜欢的CSS的所有功能,包括媒体查询,伪选择器和嵌套。

为什么使用样式组件?

创建样式组件的原因如下:

  • 没有类名地狱
    样式化的组件会为您的样式生成唯一的类名,而不是您费力地为元素查找类名。您永远不必担心拼写错误或使用没有意义的类名。
  • 使用props
    styled-components允许我们props使用React中常用的参数来扩展样式属性,从而通过应用程序的状态动态影响组件的感觉。
  • 支持Sass语法开箱即用即可
    编写Sass语法,而无需设置任何预处理程序或使用样式组件即可使用其他构建工具。在样式定义中,可以使用该&字符作为当前组件的目标,使用伪选择器,并尝试嵌套。
  • 通过导出包装器组件,主题
    样式化组件具有完全的主题化支持ThemeProvider。该组件通过Context API为自身内的所有React组件提供了主题。在渲染树中,所有样式化的组件都可以访问所提供的主题,即使它们位于多个级别。随着本教程的继续,我们将更深入地了解样式化组件的主题功能。

要了解样式化组件的更多优点,请查看Kris Guzman的文章

实施黑暗模式

在本文中,我们将在类似YouTube的简单网页上实现暗模式。

要继续进行,请确保您从starterbranch克隆了原始存储库。

配置

让我们在package.json文件中安装所有依赖项。在终端上,运行以下命令:

npm install

成功安装后,运行npm start。这是未实现暗模式的网页外观。

使用样式化组件在React Apps中实现暗模式
要使用的网页,没有暗模式。(大型预览

要安装styled-components,请在您的终端上运行npm install styled-components

实作

要实现暗模式,我们需要创建四个不同的组件。

  • 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',
}

在这里,我们定义和出口lightTheme,并darkTheme具有鲜明的颜色变量的对象。随时尝试和定制适合您的变量。

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;
  }
  `

我们是createGlobalStyle从样式组件中导入的。该createGlobalStyle方法替换了样式组件版本3中现已弃用的injectGlobal方法。此方法生成一个React组件,将其添加到组件树后,会将全局样式注入到文档中(在我们的示例中为)App.js

我们定义了一个GlobalStyle组件,backgroundcolor为主题对象的值分配了属性。因此,每次我们切换切换开关时,其值都会根据传递给ThemeProvider它的深色主题或浅色主题对象而改变(稍后将继续进行创建)。

的transition属性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。我们是ThemeProvider从导入的styled-componentsThemeProvider是样式化组件库中提供主题支持的帮助程序组件。该帮助器组件通过Context API将主题注入到其自身下方的所有React组件中。

在渲染树中,所有样式化的组件都可以访问所提供的主题,即使它们位于多个级别。查看“ 主题 ”部分。

接下来,我们GlobalStyle从中导入包装器./components/Globalstyle。最后,从顶部,我们从中导入lightThemedarkTheme对象./components/Themes

为了创建切换方法,我们需要一个保持主题初始颜色值的状态。因此,我们创建一个theme状态,并light使用useState挂钩将初始状态设置为。

现在,切换功能。

themeToggler方法使用三元运算符检查的状态theme,并根据条件的值切换暗或亮。

ThemeProvider,一个样式化组件的帮助器组件,将所有内容包装在该return语句中,并在其下方注入任何组件。请记住,我们GlobalStyles全局样式注入到我们的组件中;因此,它在ThemeProvider包装器组件内部被调用。

最后,我们创建了一个带有onClick事件的按钮,该事件将themeToggler方法分配给它。

让我们看看到目前为止的结果。

使用样式化组件在React Apps中实现暗模式
暗模式下无持久性(大预览

我们的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;

为了使内容整洁,我们Toggle使用styled样式化组件中的函数在组件中设置了切换按钮的样式。

这纯粹是为了演示;您可以根据需要设置按钮的样式。

Toggle组件内部,我们传递了两个道具:

  • theme提供当前主题(亮或暗);
  • toggleTheme功能将用于在主题之间切换。

接下来,我们返回Button组件并toggleThemeonClick事件分配一个函数。

最后,我们propTypes用来定义我们的类型,确保我们themestringand isRequired,而我们toggleThemefuncand isRequired

使用自定义挂钩(useDarkMode

在构建应用程序时,可伸缩性至关重要,这意味着我们的业务逻辑必须可重用,以便我们可以在很多地方甚至在不同的项目中使用它。

这就是为什么将切换功能移至单独的组件会很棒。为此,我们将创建自己的自定义钩子。

让我们useDarkMode.jscomponents文件夹中创建一个名为的新文件,然后进行一些调整,将逻辑移至该文件。将以下代码添加到文件中:

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,其中包含所选内容themethemeToggler在模式之间切换的函数。

我想您会同意我们的暗模式组件看起来很时尚。

让我们继续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

首先,我们导入自定义钩子,对themeand themeToggler道具进行解构,并使用useDarkMode函数进行设置。

请注意,该useDarkMode方法替换了我们theme最初位于中的状态App.js

我们声明一个themeMode变量,该变量根据当时的theme模式情况呈现浅色或深色主题。

现在,我们的ThemeProvider包装器组件将刚刚创建的themeMode变量分配给themeprop。

最后,我们传入常规Toggle组件代替常规按钮。

记住,在我们的Toggle组件中,我们定义并设置了按钮样式,并将两者theme以及toggleTheme它们作为道具传递给他们。因此,我们要做的就是将这些道具适当地传递给该Toggle组件,该组件将充当我们中的按钮App.js

是! 设置了我们的黑暗模式,并且该模式持续存在,当刷新页面或在新标签页中访问页面时,颜色不变。

让我们看看实际结果:

使用样式化组件在React Apps中实现暗模式
实现了暗模式,但在重新加载浏览器时按钮颜色出现了故障。(大型预览

几乎所有的东西都运行良好,但是我们可以做一件事来使我们的体验更加出色。切换到深色主题,然后重新加载页面。您看到按钮中的蓝色在灰色之前短暂加载了吗?发生这种情况是因为我们的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, mountedComponent] };

高亮显示的代码是唯一添加到的代码useDarkMode.js。我们创建了另一个名为的状态mountedComponent,并将默认值设置为false使用useState挂钩。接下来,在useEffect挂钩中,将mountedComponent状态设置为trueusing setMountedComponent。最后,在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;

我们已经将mountedComponent状态作为道具添加到了useDarkMode钩子中,并且检查了组件是否已安装,因为这是useEffect钩子中发生的事情。如果尚未发生,那么我们将渲染一个empty div

让我们看看暗模式网页的结果。

使用样式化组件在React Apps中实现暗模式
黑暗模式的最终结果(大预览

现在,您会注意到在暗模式下,当页面重新加载时,按钮的颜色不会改变。

结论

黑暗模式越来越成为用户的首选,ThemeProvider在样式化组件中使用主题包装器时,在React Web应用程序中实现它要容易得多。在实现暗模式时,继续尝试样式化组件。您可以添加图标而不是按钮。

请确实在下面的评论部分中使用样式化组件中的主题功能分享您的反馈和经验。我很乐意看到您的想法!

GitHub上提供了本文的支持存储库。另外,在CodeSandbox上签出。

版权所有:https://www.eraycloud.com 转载请注明出处