摘要:然后使用,在的上綁定了,很簡單。日歷遍歷的思路這一次的提交主要是確定了日歷組件,怎么寫,具體思路看下面的代碼,盡量通過注釋把思路講的清楚一些。
前言在最近的項目中,大量的嘗試了react hooks,我們的組件庫用的是Next,除了一個地方因為要使用Form + Field的組合,所以使用了class組件,經過了這個項目,也算是總結一些使用的經驗,所以準備自己封裝一個日歷組件來分享一下。以下也會通過git中的commit記錄,來分析我的思路。
下來看下效果(因為軟件的原因,轉的gif竟然如此抖動,順便求一個mac上轉換gif比較好用的軟件)
同時奉上代碼地址: 倉庫
初始化項目只是使用了create-react-app進行了項目的搭建,然后總體就是hooks + typescript,當然寫的不夠好,就沒有往npm上發的意愿。
工具方法,保存狀態首先是寫了一個工具方法,作用是將時間戳轉化成 年月日 三個屬性。
interface Res {
year: number;
month: number;
day: number;
}
export const getYearMonthDay = (value: number):Res => {
const date = new Date(value);
return {
year: date.getFullYear(),
month: date.getMonth(),
day: date.getDay()
}
}
然后使用useState,在input的dom上綁定了value,很簡單。
const Test:React.FC = memo(({value = Date.now(), onChange}) => {
console.log("render -------");
const [date, setDate] = useState<Date>(new Date(value));
const { year, month, day } = getYearMonthDay(date.getTime());
console.log(year, month, day);
return (
<div>
<input type="text" value={`${year} - ${month} - ${day}`} />
div>
)
});
日歷遍歷的思路
這一次的提交主要是確定了日歷組件,怎么寫,具體思路看下面的代碼,盡量通過注釋把思路講的清楚一些。
// 以下兩個數組只是為了遍歷
const ary7 = new Array(7).fill("");
const ary6 = new Array(6).fill("");
const Test: React.FC = memo(({ value = Date.now(), onChange }) => {
const [date, setDate] = useState<Date>(new Date(value));
// useState保存下面彈窗收起/展開的狀態
const [contentVisible, setContentVisible] = useState(true);
const { year, month, day } = getYearMonthDay(date.getTime());
// 獲取當前選中月份的第一天
const currentMonthFirstDay = new Date(year, month, 1);
// 判斷出這一天是星期幾
const dayOfCurrentMonthFirstDay = currentMonthFirstDay.getDay();
// 然后當前日歷的第一天就應該是月份第一天向前移幾天
const startDay = new Date(currentMonthFirstDay.getTime() - dayOfCurrentMonthFirstDay * 1000 * 60 * 60 * 24);
const dates:Date[] = [];
// 生成一個長度為42的數組,里面記錄了從第一天開始的每一個date對象
for (let index = 0; index < 42; index++) {
dates.push(new Date(startDay.getTime() + 1000 * 60 * 60 * 24 * index));
}
return (
<div>
<input type="text" value={`${year} - ${month} - ${day}`} />
{
contentVisible && (
<div>
<div>
<span><span>
<span>< <span>
<span>span>
<span>>span>
<span>> >span>
div>
<div>
// 生成的日歷應該是7*6的,然后遍歷出42個span, 這就是之前生成的兩個常量數組的作用
{
ary6.map((_, index) => {
return (
<div>
{
ary7.map((__, idx) => {
const num = index * 7 + idx;
console.log(num);
const curDate = dates[num]
return (
<span>{curDate.getDate()}span>
)
})
}
div>
)
})
}
div>
div>
)
}
div>
)
});
處理document點擊事件
const Test: React.FC = memo(({ value = Date.now(), onChange }) => {
// 使用useRef保存最外一層包裹的div
const wrapper = useRef(null);
// 展開收起的方法,都使用了useCallback,傳入空數組,讓每次渲染都返回相同的函數
const openContent = useCallback(
() => setContentVisible(true),
[]
);
const closeContent = useCallback(
() => setContentVisible(false),
[]
);
const windowClickhandler = useCallback(
(ev: MouseEvent) => {
let target = ev.target as HTMLElement;
if(wrapper.current && wrapper.current.contains(target)) {
} else {
closeContent();
}
},
[]
)
// 使用useEffect模擬componentDidMount和componentWillUnmount的生命周期函數,來綁定事件
useEffect(
() => {
window.addEventListener("click",windowClickhandler);
return () => {
window.removeEventListener("click", windowClickhandler);
}
},
[]
)
return (
<div ref={wrapper}>
// 之前的那些東西,沒有變化
div>
)
});
處理每個日期的點擊事件
// 使用setDate,處理,這里其實第二個參數傳遞一個空數組即可,因為這個函數是不依賴當前的date狀態來變化的。
const dateClickHandler = useCallback(
(date:Date) => {
setDate(date);
const { year, month, day } = getYearMonthDay(date.getTime());
onChange(`${year}-${month}-${day}`);
setContentVisible(false);
},
[date]
)
dateClickHandler(curDate)}>{curDate.getDate()}
支持value傳遞
// 先判斷以下value是否傳遞,如果沒傳默認就是當前的時間
const DatePicker: React.FC = ({ value = "", onChange = () => { } }) => {
let initialDate: Date;
if (value) {
let [year, month, day] = value.split("-");
initialDate = new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
} else {
initialDate = new Date();
}
const [date, setDate] = useState<Date>(initialDate);
}
月份切換的事件處理
月份處理就是當前月份的第一天向前移動一個月或者一個月等等。
const DatePicker: React.FC = ({ value = "", onChange = () => { } }) => {
// 之前這里的currentMonthFirstDay是直接由date得出的,現在成為組件的state就可以讓他支持變化。
const { year, month, day } = getYearMonthDay(date.getTime());
const [currentMonthFirstDay, setCurrentMonthFirstDay] = useState<Date>(new Date(year, month, 1));
const { year: chosedYear, month: chosedMonth } = getYearMonthDay(currentMonthFirstDay.getTime());
const dayOfCurrentMonthFirstDay = currentMonthFirstDay.getDay();
const startDay = new Date(currentMonthFirstDay.getTime() - dayOfCurrentMonthFirstDay * 1000 * 60 * 60 * 24);
const dates: Date[] = [];
for (let index = 0; index < 42; index++) {
dates.push(new Date(startDay.getTime() + 1000 * 60 * 60 * 24 * index));
}
const openContent = useCallback(
() => setContentVisible(true),
[]
);
const closeContent = useCallback(
() => setContentVisible(false),
[]
);
const windowClickhandler = useCallback(
(ev: MouseEvent) => {
let target = ev.target as HTMLElement;
if (wrapper.current && wrapper.current.contains(target)) {
} else {
closeContent();
}
},
[]
);
const dateClickHandler = useCallback(
(date: Date) => {
const { year, month, day } = getYearMonthDay(date.getTime());
onChange(`${year}-${month + 1}-${day}`);
setContentVisible(false);
},
[date]
);
// 這里所有的月份切換事件都選擇了使用了函數式更新
const prevMonthHandler = useCallback(
() => {
setCurrentMonthFirstDay(value => {
let { year, month } = getYearMonthDay(value.getTime());
if (month === 0) {
month = 11;
year--;
} else {
month--;
}
return new Date(year, month, 1)
})
},
[]
);
const nextMonthHandler = useCallback(
() => {
setCurrentMonthFirstDay(value => {
let { year, month } = getYearMonthDay(value.getTime());
if (month === 11) {
month = 0;
year++;
} else {
month++;
};
return new Date(year, month, 1);
})
},
[]
);
const prevYearhandler = useCallback(
() => {
setCurrentMonthFirstDay(value => {
let { year, month } = getYearMonthDay(value.getTime());
return new Date(--year, month, 1)
})
},
[]
);
const nextYearHandler = useCallback(
() => {
setCurrentMonthFirstDay(value => {
let { year, month } = getYearMonthDay(value.getTime());
return new Date(++year, month, 1)
})
},
[]
)
return (
<div ref={wrapper}>
<input type="text" value={`${year} - ${month + 1} - ${day}`} onFocus={openContent} />
{
contentVisible && (
<div className="content">
<div className="header">
<span onClick={prevYearhandler}>< <span>
<span onClick={prevMonthHandler}><span>
<span>{`${chosedYear} - ${chosedMonth + 1}`}span>
<span onClick={nextMonthHandler}>>span>
<span onClick={nextYearHandler}>> >span>
div>
div>
)
}
div>
)
};
處理porps變化
工具方法
export const getDateFromString = (str: string):Date => {
let [year, month, day] = str.split("-");
return new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
}
組件
const DatePicker: React.FC = ({ value = "", onChange = () => { } }) => {
let initialDate: Date;
if (value) {
initialDate = getDateFromString(value);
} else {
initialDate = new Date();
};
const [date, setDate] = useState<Date>(initialDate);
// 使用了useRef來保存上一次渲染時傳遞的value值,
const prevValue = useRef(value);
useEffect(
() => {
// 僅當value值變化且不同與上一次值時,才會重新進行改變自身date狀態。
if (prevValue.current !== value) {
let newDate = value ");new Date();
setDate(newDate);
const { year, month } = getYearMonthDay(newDate.getTime());
setCurrentMonthFirstDay(new Date(year, month, 1))
}
},
[value]
)
return (
...
)
};
最后這里現在想來其實也可以用useMemo來處理傳遞進來的value值,這也是一種思路。稍后實現一下。
最后貼下代碼github地址。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/6921.html
摘要:所以我們做的事情其實就是,聲明了一個狀態變量,把它的初始值設為,同時提供了一個可以更改的函數。 你還在為該使用無狀態組件(Function)還是有狀態組件(Class)而煩惱嗎? ——擁有了hooks,你再也不需要寫Class了,你的所有組件都將是Function。 你還在為搞不清使用哪個生命周期鉤子函數而日夜難眠嗎? ——擁有了Hooks,生命周期鉤子函數可以先丟一邊了。 你在還...
摘要:第一個功能是普通經典類組件,也就是眾所周知的有狀態組件。我們準備創建一個上下文環境來存放全局狀態,然后把它的包裹在一個有狀態組件中,然后用來管理狀態。接下來我們需要用有狀態組件包裹我們的,利用它進行應用狀態的管理。 原文地址對于想要跳過文章直接看結果的人,我已經把我寫的內容制作成了一個庫:use-simple-state,無任何依賴(除了依賴 react ),只有3kb,相當輕量。 ...
摘要:前言由于最近接到一個需要支持拖拽選擇日期的日歷需求,做出來感覺體驗和效果都還不錯,所以今天想跟大家分享一下封裝這個日歷組件的過程。其中,代表該日期當前的狀態,主要是用以區分用戶在拖拽操作日歷時,有沒有選中該日期。 1. 前言 由于最近接到一個需要支持拖拽選擇日期的日歷需求,做出來感覺體驗和效果都還不錯,所以今天想跟大家分享一下封裝這個日歷組件的過程。 2. 調研開始 正所謂磨刀不誤砍柴...
摘要:為什么選擇是開發和維護的一種面向對象的編程語言。一在組件組件復用狀態邏輯很難沒有提供將可復用性行為附加到組件的途徑例如,把組件連接到。如此很容易產生,并且導致邏輯不一致。同時,這也是很多人將與狀態管理庫結合使用的原因之一。 前端時間,接觸了hooks,研究了一段時間后感覺使用起來十分方便,正好公司開了一個新的小項目,正好使用hooks來實踐一下。 技術選型 1.為什么選擇umi 在之前...
摘要:難道還不允許設計得對新人更友好了我們先把做成就是找罵啊這怎么怪到我們頭上了事實是,即使在內部,也顯然不是所有程序員都熟悉函數式編程的概念。 1.前言介紹 歷史React在2013年開源,在2015引入函數式組件,不過在日常開發中經常被忽略。ReactJS Core Team 確實大部分成員都曾在推特上公開夸贊過對函數式編程 與 ML 系語言(或其特性)的優點:Sebastian 日常提...
閱讀 1991·2023-04-26 01:41
閱讀 2468·2021-11-24 09:39
閱讀 1922·2021-11-24 09:38
閱讀 1947·2021-11-19 09:40
閱讀 3760·2021-11-11 11:02
閱讀 3294·2021-10-20 13:48
閱讀 3156·2021-10-14 09:43
閱讀 4360·2021-09-02 15:11