Commit 8a6ebbb7 by tungnq

TODO: Bổ sung hàm check render

parent 1f57d4df
......@@ -7,8 +7,6 @@ import {
Text,
Image,
} from 'react-native';
import R from '../assets/R';
const Button = props => {
const {
title,
......
import React, { useState, useEffect, useRef } from 'react';
import React, {useState, useEffect, useRef} from 'react';
import {
View,
Text,
......@@ -10,12 +10,12 @@ import {
import Icon from 'react-native-vector-icons/MaterialIcons';
import R from '../../assets/R';
const { width: screenWidth } = Dimensions.get('window');
const {width: screenWidth} = Dimensions.get('window');
/**
* Component TabView có thể tái sử dụng
* Hỗ trợ 2 chế độ: filter (lọc dữ liệu) và navigate (điều hướng)
*
*
* Props:
* - data: Mảng dữ liệu tab [{key, label, icon?, disabled?}]
* - mode: 'filter' (lọc) hoặc 'navigate' (điều hướng)
......@@ -60,12 +60,14 @@ const TabViewComponent = ({
if (data.length === 0) return;
let initialKey = defaultActiveKey;
// Kiểm tra xem defaultActiveKey có tồn tại trong data không
if (defaultActiveKey) {
const keyExists = data.some(item => item.key === defaultActiveKey);
if (!keyExists) {
console.warn(`TabView: defaultActiveKey "${defaultActiveKey}" không tìm thấy trong data. Sử dụng tab đầu tiên.`);
console.warn(
`TabView: defaultActiveKey "${defaultActiveKey}" không tìm thấy trong data. Sử dụng tab đầu tiên.`,
);
initialKey = data[0].key;
}
} else {
......@@ -79,12 +81,14 @@ const TabViewComponent = ({
// Kiểm tra key trùng lặp trong data
useEffect(() => {
if (data.length === 0) return;
const keys = data.map(item => item.key);
const uniqueKeys = [...new Set(keys)];
if (keys.length !== uniqueKeys.length) {
console.warn('TabView: Phát hiện key trùng lặp trong mảng data. Có thể gây lỗi không mong muốn.');
console.warn(
'TabView: Phát hiện key trùng lặp trong mảng data. Có thể gây lỗi không mong muốn.',
);
}
}, [data]);
......@@ -101,19 +105,19 @@ const TabViewComponent = ({
const tabCenter = x + width / 2; // Tâm của tab
const screenCenter = screenWidth / 2; // Tâm màn hình
const scrollToX = Math.max(0, tabCenter - screenCenter); // Vị trí cần cuộn
scrollViewRef.current.scrollTo({
x: scrollToX,
animated: true,
});
},
() => {} // Callback khi đo thất bại
() => {}, // Callback khi đo thất bại
);
}
}, [activeKey, scrollable]);
// Xử lý khi người dùng nhấn vào tab
const handleTabPress = (item) => {
const handleTabPress = item => {
// Không làm gì nếu tab bị disabled
if (item.disabled) return;
......@@ -143,7 +147,7 @@ const TabViewComponent = ({
return (
<TouchableOpacity
key={item.key}
ref={ref => tabRefs.current[item.key] = ref} // Lưu ref để dùng cho auto scroll
ref={ref => (tabRefs.current[item.key] = ref)} // Lưu ref để dùng cho auto scroll
style={[
styles.tab, // Style cơ bản
tabStyle, // Style tùy chỉnh từ props
......@@ -153,8 +157,7 @@ const TabViewComponent = ({
]}
onPress={() => handleTabPress(item)}
disabled={isDisabled}
activeOpacity={0.7}
>
activeOpacity={0.7}>
<View style={styles.tabContent}>
{/* Hiển thị icon nếu có */}
{item.icon && (
......@@ -169,25 +172,24 @@ const TabViewComponent = ({
<Text
style={[
styles.tabText, // Style text cơ bản
{ color: isActive ? activeColor : inactiveColor}, // Dùng màu từ props
{color: isActive ? activeColor : inactiveColor}, // Dùng màu từ props
textStyle, // Style text tùy chỉnh
isActive && styles.activeTabText, // Style text khi active
isActive && activeTextStyle, // Style text active tùy chỉnh
isDisabled && styles.disabledTabText, // Style text khi disabled
]}
numberOfLines={1}
>
numberOfLines={1}>
{/* Ưu tiên label, sau đó title, name, cuối cùng là 'Tab' */}
{item.label || item.title || item.name || 'Tab'}
</Text>
</View>
{/* Thanh gạch dưới khi tab active */}
{isActive && showActiveIndicator && (
<View
<View
style={[
styles.activeIndicator,
{ backgroundColor: activeIndicatorColor || activeColor } // Dùng màu từ props
]}
styles.activeIndicator,
{backgroundColor: activeIndicatorColor || activeColor}, // Dùng màu từ props
]}
/>
)}
</TouchableOpacity>
......@@ -222,7 +224,7 @@ const TabViewComponent = ({
<View style={[styles.container, style]}>
<TabContainer {...containerProps}>
{data.map((item, index) => renderTab(item, index))}
</TabContainer>
</TabContainer>
</View>
);
};
......@@ -306,4 +308,4 @@ const styles = StyleSheet.create({
});
// Export component để sử dụng ở nơi khác
export default TabViewComponent;
export default React.memo(TabViewComponent);
import React from 'react';
import React, { useRef, useEffect } from 'react';
import {
Dimensions,
Platform,
......@@ -234,9 +234,8 @@ export const removeItemFromArr2 = (items, index) => {
return fill;
};
export const isValidEmail = (email) =>
email.length > 0 && /(.+)@(.+){2,}\.(.+){2,}/gmi.test(email);
export const isValidEmail = email =>
email.length > 0 && /(.+)@(.+){2,}\.(.+){2,}/gim.test(email);
export const removeItemFromArr = (items, index) => {
items.splice(index, 1);
......@@ -607,7 +606,7 @@ export const getMimeType = fileExt => {
};
//Calendar
export const parseMinutes = (timeStr) => {
export const parseMinutes = timeStr => {
if (!timeStr || typeof timeStr !== 'string') return 0;
const parts = timeStr.split(':');
if (parts.length !== 2) return 0;
......@@ -616,7 +615,7 @@ export const parseMinutes = (timeStr) => {
return h * 60 + m;
};
export const formatDateToString = (date) => {
export const formatDateToString = date => {
const y = date.getFullYear();
const m = String(date.getMonth() + 1).padStart(2, '0');
const d = String(date.getDate()).padStart(2, '0');
......@@ -652,7 +651,7 @@ export const layoutDayEvents = (events, hourHeight = 80) => {
let cur = null;
for (const ev of mapped) {
if (!cur || ev.start >= cur.maxEnd) {
cur = { id: groups.length, items: [ev], maxEnd: ev.end };
cur = {id: groups.length, items: [ev], maxEnd: ev.end};
groups.push(cur);
} else {
cur.items.push(ev);
......@@ -664,8 +663,8 @@ export const layoutDayEvents = (events, hourHeight = 80) => {
// Gán cột theo greedy interval partitioning
for (const g of groups) {
const colEnds = []; // end time cuối mỗi cột
const colOf = {}; // event.id -> columnIndex
const colEnds = []; // end time cuối mỗi cột
const colOf = {}; // event.id -> columnIndex
const items = [...g.items].sort((a, b) => a.start - b.start);
for (const ev of items) {
......@@ -709,4 +708,19 @@ export const layoutDayEvents = (events, hourHeight = 80) => {
// Trả về theo thứ tự ban đầu để render ổn định
return out.sort((a, b) => a._idx - b._idx);
};
\ No newline at end of file
/**
* Hook đếm số lần render của một component
*/
};
export const useRenderCount = (name = 'Component') => {
const renderCount = useRef(0);
renderCount.current += 1;
useEffect(() => {
console.log(`${name} đã render ${renderCount.current} lần`);
});
return renderCount.current;
};
......@@ -176,6 +176,7 @@ const ListWorkView = props => {
const renderBody = () =>{
return (
<View style={styles.body}>
{renderTabView()}
{renderCard()}
<View style={styles.listContainer}>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment