2k1
  • Home
  • Programming
  • System
  • Design
  • Applications
  • Tech
No Result
View All Result
  • Login
2k1
  • Home
  • Programming
  • System
  • Design
  • Applications
  • Tech
No Result
View All Result
2k1
No Result
View All Result

Tạo ứng dụng pomodoro trong NextJS

Nguyen Pham by Nguyen Pham
10/06/2021
in Blog
Reading Time: 6 mins read
A A
0

Giới thiệu

Pomodoro là một phương pháp học tập mà chúng ta sẽ tập trung học trong 25 phút sau đó sẽ có một khoảng nghỉ là 5 phút.

Chúng ta sẽ tạo một app pomodoro để thực hiện việc đếm giờ đó.

Bắt đầu code

Chúng ta sẽ sử dụng NextJS và TailwindCSS. Để bắt đầu chúng ta có thể clone repo sau:

git clone https://github.com/nvni/next-tailwindcss.git pomodoro
cd pomodoro
yarn install

Để hiểu chi tiết cách cài đặt bạn có thể xem bài viết này: https://2k1.org/su-dung-tailwind-css-trong-nextjs/1276/2021/

Bắt đầu code với file index.js

import { useEffect, useState } from "react";
import Head from "next/head";

const timeType = {
  pomodoro: { time: 25 * 60, stopmessage: "Bạn đã hoàn thành pomodoro 🍅" },
  sbreak: {
    time: 5 * 60,
    stopmessage: "Bạn đã hết thời gian nghỉ bắt đầu làm việc thôi nào 😊",
  },
  lbreak: {
    time: 15 * 60,
    stopmessage: "Bạn đã hết thời gian nghỉ bắt đầu làm việc thôi nào 😉",
  },
};
function pomodoro() {
  const [time, setTime] = useState(25 * 60);
  const [active, setActive] = useState(false);
  const [inter, setInter] = useState();
  const [type, setType] = useState("pomodoro");
  const [currentTime, setCurrentTime] = useState();
  // Change type
  useEffect(() => {
    timeReset();
  }, [type]);

  // Send notification
  useEffect(async () => {
    console.log("time :>> ", time);
    if (time === 0) {
      if (Notification.permission == "granted") {
        navigator.serviceWorker.getRegistration().then(function (reg) {
          reg.showNotification(timeType[type].stopmessage);
        });
      }
      clearInterval(inter);
    }
  }, [time]);

  // Active time
  useEffect(() => {
    if (active) {
      setInter(
        setInterval(() => {
          setTime(
            Math.floor(
              timeType[type].time - (new Date().getTime() - currentTime) / 1000
            )
          );
          console.log(
            "((new Date().getTime()) - currentTime) :>> ",
            Math.floor(
              timeType[type].time - (new Date().getTime() - currentTime) / 1000
            )
          );
        }, 100)
      );
    }
    return () => {
      clearInterval(inter);
    };
  }, [active]);

  function timeStart() {
    setCurrentTime(new Date().getTime());
    if (Notification.permission != "granted") {
      alert("You need turn on Notification");
      Notification.requestPermission(function (status) {
        console.log("Notification permission status:", status);
      });
    }
    setActive((a) => !a);
  }

  function timeStop() {
    clearInterval(inter);
    setActive(false);
  }

  // resetTime
  function timeReset() {
    clearInterval(inter);
    setActive(false);
    setTime(timeType[type].time);
  }
  return (
    <div>
      <Head>
        <title>{type}</title>
        {type == "pomodoro" && (
          <link rel="icon" href="/1.svg" type="image/svg" sizes="16x16" />
        )}
        {type == "sbreak" && (
          <link rel="icon" href="/2.svg" type="image/svg" sizes="16x16" />
        )}
        {type == "lbreak" && (
          <link rel="icon" href="/3.svg" type="image/svg" sizes="16x16" />
        )}
      </Head>
      <div className="flex h-screen w-screen bg-gray-700  justify-center items-center">
        <div className="flex flex-col w-full min-w-md md:w-1/2 h-4/5 bg-white rounded-lg">
          <div className="flex flex-row w-full justify-between p-5 space-x-2">
            <div
              onClick={() => {
                setType("pomodoro");
              }}
              className={`flex-1 text-center text-xl border rounded-lg hover:bg-red-300 p-1 ${
                type == "pomodoro" && "bg-red-300"
              }`}
            >
              Pomodoro
            </div>
            <div
              onClick={() => {
                setType("sbreak");
              }}
              className={`flex-1 text-center text-xl border rounded-lg hover:bg-green-300 p-1 ${
                type == "sbreak" && "bg-green-300"
              }`}
            >
              Nghỉ
            </div>
            <div
              onClick={() => {
                setType("lbreak");
              }}
              className={`flex-1  text-center text-xl border rounded-lg hover:bg-blue-300 p-1  ${
                type == "lbreak" && "bg-blue-300"
              }`}
            >
              Nghỉ dài
            </div>
          </div>
          <div className="w-full text-9xl text-center">
            {`${parseInt(time / 60)
              .toString()
              .padStart(2, "0")} : ${(time % 60).toString().padStart(2, "0")}`}
          </div>
          {/* Control button */}
          <div className="w-full text-9xl text-center space-x-4">
            <button
              className="text-5xl p-5 rounded-lg border border-blue-400 hover:bg-blue-400 hover:text-white focus:outline-none"
              style={active ? { display: "none" } : { display: "initial" }}
              onClick={timeStart}
            >
              Start
            </button>
            <button
              className="text-5xl p-5 rounded-lg border border-yellow-400 hover:bg-yellow-400 hover:text-white focus:outline-none"
              style={active ? { display: "initial" } : { display: "none" }}
              onClick={timeStop}
            >
              Stop
            </button>

            <button
              className="text-5xl p-5 rounded-lg border border-red-400 hover:bg-red-400 hover:text-white focus:outline-none"
              onClick={timeReset}
            >
              Reset
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}

export default pomodoro;

Một vài vấn đề cần lưu ý:

Các hàm setState là không đồng bộ
tham khảo https://medium.com/@baphemot/understanding-reactjs-setstate-a4640451865b

Muốn cập nhật dữ liệu state từ state cũ ta cần làm setState(prevState => prevState+1) Điều này là bắt buộc.

do vậy khi ta muốn một state thay đổi và lấy gia trị của state thì cần cho vào hàm useEffect(()=>{} , [state])

Hiểu về cleanup effect:

Tài liệu: https://vi.reactjs.org/docs/hooks-effect.html

  useEffect(() => {
    // effect
    return () => {
      // clean
    };
  }, []);

Tại sao phải clean:

  • Khi ta áp dụng một effect thì effect nó sẽ tồn tại. như chương trình trên thì ta có setInterval Nếu không clean thì khi gọi effect lần thứ 2 nó vẫn áp dụng tiếp => nó áp dụng 2 lần setInterval do vậy sau mỗi lần ta cân clean nó đi.
  • Cách chạy nó như sau:
    • lần 1: effect (Khi component mount)
    • lần 2: clean effect -> effect
    • lần 3: clean effect -> effect
    • …..

Một số vấn đề với setInterval

setInterval(()=>{
// Code
},1000)

setInterval sẽ thực hiện code trong 1s nhưng khi đếm thời gian thì nó bị ảnh hưởng bởi một số hàm khác nên nó sẽ bị đếm thời gian sai: do vậy ở code trên ta sẽ thực hiện update theo thời gian lấy từ hàm Date() khoảng thời gian update là 100ms

tham khảo: https://stackoverflow.com/questions/6685396/execute-the-setinterval-function-without-delay-the-first-time

Hiển thị notification

https://developers.google.com/web/ilt/pwa/introduction-to-push-notifications

Sử dụng PWA

https://github.com/shadowwalker/next-pwa

  • Lưu ý file manifest,json cần đầy thủ thông tin thì mới có thể dùng PWA được

Tham khảo thêm:

  • https://ozmoroz.com/2018/11/why-my-setstate-doesnt-work/
  • https://viblo.asia/p/nhung-dieu-can-luu-y-va-su-dung-hook-trong-react-phan-2-gDVK2Ln0lLj
Previous Post

Cách cài đặt và cập nhật driver đúng nhất.

Next Post

Tạo layout trong NextJS

Related Posts

Blog

Facebook, Instagram bất ngờ sập trên diện rộng, liên tục đăng xuất người dùng!

by Nguyen Pham
05/03/2024
Xây dựng todo app với smartcontract
Blog

Web3 là gì?

by Nguyen Pham
30/06/2023
Blog

Chạy ứng dụng react native đầu tiên của bạn

by Nguyen Pham
29/06/2023
Giới thiệu về ChatGPT
Blog

Cách tạo tài khoản ChatGPT

by Nguyen Pham
16/05/2023
Blog

Làm thế nào để tìm lại những trang web hay mà mình đã từng truy cập

by Nguyen Pham
06/05/2021
Blog

Cách tạo một phần mềm quản lý tài chính mà không cần code.

by Nguyen Pham
16/11/2022
Load More
Next Post

Tạo layout trong NextJS

Please login to join discussion
Stock

Phân tích mã cổ phiếu VCB

by Nguyen Pham
26/04/2025
0

Phân tích mã cổ phiếu VCB (Ngân hàng TMCP Ngoại thương Việt Nam - Vietcombank) 1. Tổng quan về Vietcombank...

Read more

Facebook, Instagram bất ngờ sập trên diện rộng, liên tục đăng xuất người dùng!

05/03/2024
Xây dựng todo app với smartcontract

Web3 là gì?

30/06/2023
Xây dựng todo app với smartcontract

Giới thiệu về đa luồng trong ngôn ngữ lập trình go có code minh họa.

29/06/2023

Chạy ứng dụng react native đầu tiên của bạn

29/06/2023

@2021 2k1.org [email protected]

No Result
View All Result
  • Home
  • Review
  • Applications
  • Computers
  • Gaming
  • Microsoft

© 2021 NData

Welcome Back!

Login to your account below

Forgotten Password?

Retrieve your password

Please enter your username or email address to reset your password.

Log In