- vừa được xem lúc

[ReactJS] Tạo Chức Năng Dark Mode Đơn Giản.

0 0 12

Người đăng: Mister Brian

Theo Viblo Asia

Mình có tìm hiểu thêm về tạo Dark Mode trong ReactJS kết hợp cùng Redux và TailWindCSS nên hôm nay cũng muốn được chia sẻ với mọi người một chút.

Let's go!

1. Triển khai ứng dụng và cài đặt một số thư viện hỗ trợ:

npx create-react-app dark-mode-app --template redux-typescript // Mình dùng yarn thay cho npm nhé.
npm install --global yarn yarn add -D @babel/plugin-proposal-private-property-in-object autoprefixer eslint eslint-config-prettier postcss styled-components tailwindcss

2. Khởi tạo cấu hình cho eslint, prettier:

// Khởi tạo file config eslintrc.js
yarn run eslint --init
Chọn "To check syntax and find problems" -> "JavaScript modules (import/export)" -> "React" -> "Yes" -> "Browser" -> "JSON" -> "npm". Tổ hợp các lựa chọn trên sẽ tự động cài đặt cho các bạn một số thư viện đi kèm của eslint:
eslint-plugin-react@latest, @typescript-eslint/eslint-plugin@latest, @typescript-eslint/parser@latest // Cài đặt thêm thư viện của prettier và eslint
yarn add -D prettier eslint-plugin-react-hooks // Tạo file .prettierrc.json ngang hàng với file package.json và thêm đoạn mã ví dụ dưới đây để tinh chỉnh format code theo ý muốn
{ "trailingComma": "es5", "tabWidth": 4, "semi": false, "singleQuote": true
} // Tạo file .prettierignore và thêm đoạn mã dưới đây để loại trừ một số thư mục cho quá trình artifacts (nếu có)
node_modules
# Ignore artifacts:
build
Coverage // Cài đặt thêm 2 thư việc sau để cấu hình cho eslint và prettier làm việc với nhau
yarn add -D eslint-config-prettier eslint-plugin-prettier // Quay lại file eslintrc.js, do mình làm đơn giản nên thêm 2 rules này vào eslintrc.js để ignore một số lỗi:
"react/react-in-jsx-scope": "off",
"react/jsx-uses-react": "off", // Đi đến file package.json, chúng ta thêm vào phần scripts các cấu hình lệnh để lint và format codes như sau:
"lint": "eslint \"src/**/*.{js,jsx,ts,tsx}\"",
"lint:fix": "eslint --fix \"src/**/*.{js,jsx,ts,tsx}\"",
"format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,css,md}\" --config ./.prettierrc.json" // Cùng file package.json các bạn có thể thêm cấu hình sau để đặt phạm vi sử dụng phiên bản của node và npm, đây là cấu hình ví dụ, bạn có thể tìm hiểu thêm [tại đây](https://docs.npmjs.com/cli/v10/configuring-npm/package-json) "engines": { "node": ">=0.10.3 <15" "npm": "~1.0.20" }

3. Khởi tạo cấu hình cho tailwindcss:

// Trước đó chúng ta đã cài đặt các thư viện cần thiết của tailwindcss, bây giờ chúng ta sẽ triển khai cấu hình theo lệnh sau:
npx tailwindcss init // Sau khi chạy lệnh trên, hệ thống sẽ tạo cho chúng ta một file cấu hình tailwind.config.js của tailwindcss, chúng ta mở file lên và cấu hình cơ bản như sau:
/** @type {import('tailwindcss').Config} */
module.exports = { content: ['./src/**/*.{js,jsx,ts,tsx}'], darkMode: 'class', theme: { extend: {}, }, variants: {}, plugins: [],
} // Bây giờ chúng ta mở file index.css và thay thế toàn bộ nội dung hiện tại bằng các layers và import mặc định của tailwindcss.
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities"; // Tiếp theo ta mở file App.tsx lên và import file index.css vào, như vậy chúng ta có thể sử dụng tailwindcss được rồi.
import "./index.css"

4. Triển khai cơ bản cấu trúc thư mục dự án như sau:

5. Triển khai chi tiết:

// Trước tiên chúng ta truy cập vào thư mục src/ tạo một thư mục utils/ và file theme.js có nội dung như bên dưới, nhìn code này chắc các bạn đã hiểu để làm gì nên mình sẽ không giải thích về nó:
export const lightTheme = { body: 'white',
}; export const darkTheme = { body: 'black',
}; // Trong thư mục src/ chúng ta tạo một thư mục styles/ và file global.ts để làm style chung có tính chuyển đổi khi chúng ta chuyển sang dark mode với nội dung sau:
import { createGlobalStyle } from 'styled-components'; export const GlobalStyleProvider = createGlobalStyle` *, *::after, *::before { box-sizing: border-box; } body { align-items: center; background: ${({ theme }) => theme.body}; color: ${({ theme }) => theme.text}; display: flex; flex-direction: column; justify-content: center; margin: 0; padding: 0; font-family: BlinkMacSystemFont, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; transition: all 0.25s linear; }
`; // Chúng ta vào thư mục redux/ hoặc store/ tạo một thư mục reducers và file themeSlice.ts để xử lý redux state logic khi thay đổi mode của theme với nội dung sau:
import { PayloadAction, createSlice } from '@reduxjs/toolkit'; export interface ThemeState { mode: boolean;
} const initialState: ThemeState = { mode: false,
}; export const themeSlice = createSlice({ name: 'theme', initialState: initialState, reducers: { toggleTheme: ( state, { payload: { mode } }: PayloadAction<ThemeState> ) => { switch (mode) { case false: state.mode = true; localStorage.setItem('theme', 'dark'); document.documentElement.classList.add('dark'); break; case true: state.mode = false; localStorage.setItem('theme', 'light'); document.documentElement.classList.remove('dark'); break; default: state.mode = false; localStorage.setItem('theme', 'light'); document.documentElement.classList.remove('dark'); break; } }, },
}); export const { toggleTheme } = themeSlice.actions;
export default themeSlice.reducer;
*Chú thích: Giá trị khởi tại ban đầu sẽ là Light Mode, khi chúng ta bật Dark Mode thì state sẽ được cập nhật lại giá trị cũng như ở phía localstatorage và đồng thời sẽ tạo một css custom class "dark" để chúng ta thay đổi giá trị style trong css global và các giá trị khác bên ngoài khi thay đổi mode. // Tiếp đến chúng tạo một file có tên reducer.ts, file này sẽ là file chính chứa tập hợp các reducers/slices con khác và gửi chúng đến store:
import { combineReducers } from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';
import themeReducer from './reducers/themeSlice'; const rootReducer = combineReducers({ counter: counterReducer, theme: themeReducer,
}); export default rootReducer; // Sau khi đã có rootReducer vừa được xuất ra, chúng ta sẽ mở file store.ts và thêm rootReducer và như sau:
import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit';
import rootReducer from './reducer'; export const store = configureStore({ reducer: rootReducer,
}); export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;
export type AppThunk<ReturnType = void> = ThunkAction< ReturnType, RootState, unknown, Action<string>
>; // Như vậy về phần xử lý state login chúng ta tạm xong, tiếp theo cũng tại src/ chúng ta sẽ tạo thư mục components/theme và file themeToggle.component.tsx trong theme/ với nội dung như sau:
import { useDispatch, useSelector } from 'react-redux';
import { MdOutlineDarkMode } from "react-icons/md"; import { MdOutlineLightMode } from "react-icons/md"; import { toggleTheme } from '../../redux/reducers/themeSlice';
import { RootState } from '../../redux/store';
import { useThemeHook } from '../../hooks/theme/theme'; export default function ToggleTheme() { const theme: boolean = useSelector((state: RootState) => state.theme.mode); const dispatch = useDispatch(); return ( theme ? <MdOutlineLightMode className=" dark:cursor-pointer dark:text-sky-500 dark:text-4xl dark:bg-transparent dark:border dark:rounded-lg dark:border-gray-800 dark:hover:bg-stone-900 dark:hover:animate-bounce w-10 h-10 " onClick={() => dispatch(toggleTheme({mode: theme}))} /> : <MdOutlineDarkMode className=" cursor-pointer text-sky-500 text-4xl bg-transparent border rounded-lg border-gray hover:bg-gray-100 hover:animate-bounce w-10 h-10 " onClick={() => dispatch(toggleTheme({mode: theme}))}/> ); }
*Chú thích: 2 nút trên sẽ được thay đổi theo trạng thái của state khi chúng ta thay đổi. // Bây giờ chúng ta sẽ mở file App.tsx và triển khai component chúng ta vừa tạo cũng như các component nhỏ cơ bản khác với nội dung sau:
import "./index.css"
import { useSelector } from 'react-redux';
import { RootState } from './redux/store'; import ToggleTheme from './components/theme/themeToggle.component';
import { darkTheme, lightTheme } from "./utils/theme";
import { GlobalStyleProvider } from "./styles/global";
import { ThemeProvider } from "styled-components"; export default function App() { const theme: boolean | null | undefined = useSelector((state: RootState) => state.theme.mode) return ( <ThemeProvider theme={theme ? darkTheme : lightTheme}> <GlobalStyleProvider /> <h1 className="flex justify-center mt-80 mb-5 text-6xl text-orange-500 dark:text-green-500 dark:text-6x"> {theme ? "DARK" : "BRIGHT"} </h1> <span className="flex justify-center"> <ToggleTheme /> </span> </ThemeProvider> );
}
*Chú thích: Tại đây ThemeProvider sẽ tiếp nhận style của darkTheme hoặc lightTheme mà chúng ta đã tạo ban đầu theo trạng thái của state và truyền đến component con GlobalStyleProvider để nhận các giá trị style và cập nhật lại theme cho chúng ta.

6. Thêm Hook ghi nhận và bảo toàn trạng thái phía client khi đóng/mở lại trình duyệt:

// Bây giờ tại thư mục src/ chúng ta tạo 2 thư mục có tên hooks/theme và theme.js bên trong theme/ có nội dung như sau:
import { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { toggleTheme } from '../../redux/reducers/themeSlice'; export const useThemeHook = () => { const dispatch = useDispatch(); useEffect(() => { if (localStorage.getItem('theme') === 'dark') { localStorage.setItem('theme', 'dark'); document.documentElement.classList.add('dark'); dispatch(toggleTheme({ mode: true })); } else { localStorage.setItem('theme', 'light'); document.documentElement.classList.remove('dark'); dispatch(toggleTheme({ mode: false })); } }, []);
}; // Cuối cùng chúng ta vào lại file themeToggle.component.tsx trong thư mục components/theme và thêm hook về như sau:
export default function ToggleTheme() { const theme: boolean = useSelector((state: RootState) => state.theme.mode); const dispatch = useDispatch(); useThemeHook(); ...

Vậy là chúng ta đã hoàn thành chức năng Dark Mode nổi tiếng theo cách đơn sơ dễ hiểu rồi, mình không pro nên có chỗ nào thiếu sót mong các bạn bỏ qua và hướng dẫn mình thêm nhé, xin đa tạ các bạn rất nhiều vì đã theo dỗi bài viết của mình !!!

Bình luận

Bài viết tương tự

- vừa được xem lúc

Utility-first CSS - Xây dựng giao diện từ những viên gạch nhỏ nhất

Mở đầu. Là một web developer chắc hẳn ai cũng đã từng phải làm việc với CSS.

0 0 45

- vừa được xem lúc

Tailwind CSS v2 có gì mới?

I. Lời mở đầu. . Tailwindcss - một utility-first CSS framework, đang dần trở nên phổ biến trong giới lập trình frontend với 34.

0 0 37

- vừa được xem lúc

TailwindsCSS có gì hay ?

1. Mở đầu. . Đổi với các bạn làm việc bên phía front-end hoặc thậm chí cả back-end thì cái tên Bootstrap không còn xa lạ gì với chúng ta.

0 0 40

- vừa được xem lúc

Hướng Dẫn Tạo Mega Menu Và Dropdown Menu Với Tailwind CSS

Trong video này mình sẽ hướng dẫn các bạn cách tạo mega menu và dropdown menu với Tailwind CSS. . Like và Subscribe ủng hộ mình nhé . .

0 0 165

- vừa được xem lúc

Make Facebook Responsive UI and Dark Mode using TailwindCSS

Make Facebook Home Page with Responsive and Dark Mode using TailwindCSS. . In this video, we will code Facebook UI using TailwindCSS. Responsive and Dark Mode included.

0 0 79

- vừa được xem lúc

Cùng thử tái hiện bố cục Facebook cover picture bằng TailwindCSS

Có bao giờ bạn thắc mắc rằng liệu có khó để tạo ra các bố cục độc đáo, tương tự như phần "ảnh bìa" ở trang cá nhân Facebook, Twitter hay không? Câu trả lời là không hề khó chút nào, với một chút kiến

0 0 32