You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

362 lines
11 KiB

import React, { useState, useEffect, useCallback } from 'react';
import {
View,
Text,
StyleSheet,
TextInput,
TouchableOpacity,
FlatList,
Keyboard,
Platform,
SafeAreaView,
StatusBar
} from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import Ionicons from '@expo/vector-icons/Ionicons';
import { useNavigation, useFocusEffect } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
// 图标组件 - 使用React.memo优化渲染
const IconComponent = React.memo(({ name, size, color }: { name: string; size: number; color: string }) => {
const Icon = Ionicons as any;
return <Icon name={name} size={size} color={color} />;
});
// 搜索历史存储键
const SEARCH_HISTORY_KEY = 'search_history';
// 历史记录项组件 - 使用React.memo优化渲染
const HistoryItem = React.memo(({
item,
onPress,
onRemove
}: {
item: string;
onPress: (item: string) => void;
onRemove: (item: string) => void
}) => (
<View style={styles.historyItemContainer}>
<TouchableOpacity
style={styles.historyItem}
onPress={() => onPress(item)}
>
<IconComponent name="time-outline" size={18} color="#999" />
<Text style={styles.historyItemText}>{item}</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => onRemove(item)}>
<IconComponent name="close" size={18} color="#999" />
</TouchableOpacity>
</View>
));
// 热门标签组件 - 使用React.memo优化渲染
const HotTagItem = React.memo(({
tag,
onPress
}: {
tag: string;
onPress: (tag: string) => void
}) => (
<TouchableOpacity
style={styles.hotSearchTag}
onPress={() => onPress(tag)}
>
<Text style={styles.hotSearchTagText}>{tag}</Text>
</TouchableOpacity>
));
export const SearchScreen = () => {
const [searchText, setSearchText] = useState('');
const [searchHistory, setSearchHistory] = useState<string[]>([]);
const [isLoading, setIsLoading] = useState(true);
const navigation = useNavigation<NativeStackNavigationProp<any>>();
// 预设热门搜索标签
const hotSearchTags = ['手机', '耳机', '电脑', '平板', '手表', '相机', '家电', '食品'];
// 只在页面聚焦时加载历史记录,而不是每次组件挂载
useFocusEffect(
useCallback(() => {
loadSearchHistory();
}, [])
);
// 从AsyncStorage加载搜索历史 - 优化异步操作
const loadSearchHistory = async () => {
try {
setIsLoading(true);
const historyJson = await AsyncStorage.getItem(SEARCH_HISTORY_KEY);
if (historyJson) {
const history = JSON.parse(historyJson);
setSearchHistory(history);
}
} catch (error) {
console.error('Failed to load search history:', error);
} finally {
setIsLoading(false);
}
};
// 保存搜索历史 - 使用useCallback优化函数引用
const saveSearchHistory = useCallback(async (searchTerm: string) => {
try {
// 如果搜索词为空,不保存
if (!searchTerm.trim()) return;
// 创建新的历史记录,将新搜索词放在最前面
const newHistory = [searchTerm, ...searchHistory.filter(item => item !== searchTerm)];
// 只保留最近10条
const trimmedHistory = newHistory.length > 10 ? newHistory.slice(0, 10) : newHistory;
// 先更新UI状态,再进行异步存储操作
setSearchHistory(trimmedHistory);
// 异步存储操作不阻塞UI
AsyncStorage.setItem(SEARCH_HISTORY_KEY, JSON.stringify(trimmedHistory))
.catch(error => console.error('Failed to save search history:', error));
} catch (error) {
console.error('Failed to process search history:', error);
}
}, [searchHistory]);
// 清除指定的搜索历史 - 使用useCallback优化函数引用
const removeSearchHistoryItem = useCallback(async (searchTerm: string) => {
try {
const newHistory = searchHistory.filter(item => item !== searchTerm);
// 先更新UI状态,再进行异步存储操作
setSearchHistory(newHistory);
// 异步存储操作不阻塞UI
AsyncStorage.setItem(SEARCH_HISTORY_KEY, JSON.stringify(newHistory))
.catch(error => console.error('Failed to remove search history item:', error));
} catch (error) {
console.error('Failed to remove search history item:', error);
}
}, [searchHistory]);
// 清除所有搜索历史 - 使用useCallback优化函数引用
const clearAllSearchHistory = useCallback(async () => {
try {
// 先更新UI状态,再进行异步存储操作
setSearchHistory([]);
// 异步存储操作不阻塞UI
AsyncStorage.removeItem(SEARCH_HISTORY_KEY)
.catch(error => console.error('Failed to clear search history:', error));
} catch (error) {
console.error('Failed to clear search history:', error);
}
}, []);
// 处理搜索提交 - 使用useCallback优化函数引用
const handleSearch = useCallback(() => {
if (searchText.trim()) {
saveSearchHistory(searchText.trim());
Keyboard.dismiss();
// 导航到搜索结果页面,并传递搜索关键词
navigation.navigate('SearchResult', { keyword: searchText.trim() });
}
}, [searchText, saveSearchHistory, navigation]);
// 点击历史记录项 - 使用useCallback优化函数引用
const handleHistoryItemPress = useCallback((item: string) => {
setSearchText(item);
saveSearchHistory(item);
// 导航到搜索结果页面,并传递搜索关键词
navigation.navigate('SearchResult', { keyword: item });
}, [saveSearchHistory, navigation]);
// 渲染历史记录项 - 使用useCallback优化函数引用
const renderHistoryItem = useCallback(({ item }: { item: string }) => (
<HistoryItem
item={item}
onPress={handleHistoryItemPress}
onRemove={removeSearchHistoryItem}
/>
), [handleHistoryItemPress, removeSearchHistoryItem]);
// 处理热门标签点击 - 使用useCallback优化函数引用
const handleHotTagPress = useCallback((tag: string) => {
setSearchText(tag);
saveSearchHistory(tag);
// 导航到搜索结果页面,并传递搜索关键词
navigation.navigate('SearchResult', { keyword: tag });
}, [saveSearchHistory, navigation]);
return (
<SafeAreaView style={styles.safeArea}>
<StatusBar barStyle="dark-content" backgroundColor="#fff" />
<View style={styles.container}>
{/* 搜索栏 */}
<View style={styles.searchHeader}>
<View style={styles.searchBar}>
<IconComponent name="search-outline" size={20} color="#999" />
<TextInput
style={styles.searchInput}
placeholder="搜索商品"
placeholderTextColor="#999"
value={searchText}
onChangeText={setSearchText}
returnKeyType="search"
onSubmitEditing={handleSearch}
autoFocus={true}
/>
{searchText.length > 0 && (
<TouchableOpacity onPress={() => setSearchText('')}>
<IconComponent name="close-circle" size={20} color="#999" />
</TouchableOpacity>
)}
</View>
<TouchableOpacity style={styles.cancelButton} onPress={() => navigation.goBack()}>
<Text style={styles.cancelButtonText}></Text>
</TouchableOpacity>
</View>
{/* 历史搜索记录 */}
{!isLoading && searchHistory.length > 0 && (
<View style={styles.historyContainer}>
<View style={styles.historyHeader}>
<Text style={styles.historyTitle}></Text>
<TouchableOpacity onPress={clearAllSearchHistory}>
<IconComponent name="trash-outline" size={20} color="#999" />
</TouchableOpacity>
</View>
<FlatList
data={searchHistory}
keyExtractor={(item, index) => `history-${index}`}
renderItem={renderHistoryItem}
style={styles.historyList}
keyboardShouldPersistTaps="handled"
initialNumToRender={5}
maxToRenderPerBatch={10}
removeClippedSubviews={true}
windowSize={5}
/>
</View>
)}
{/* 热门搜索 */}
<View style={styles.hotSearchContainer}>
<Text style={styles.hotSearchTitle}></Text>
<View style={styles.hotSearchTags}>
{hotSearchTags.map((tag, index) => (
<HotTagItem
key={`hot-${index}`}
tag={tag}
onPress={handleHotTagPress}
/>
))}
</View>
</View>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
safeArea: {
flex: 1,
backgroundColor: '#ffffff',
},
container: {
flex: 1,
backgroundColor: '#ffffff',
},
searchHeader: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 15,
paddingVertical: 10,
borderBottomWidth: 1,
borderBottomColor: '#f0f0f0',
},
searchBar: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#f5f5f5',
borderRadius: 20,
paddingHorizontal: 15,
height: 40,
},
searchInput: {
flex: 1,
marginLeft: 8,
fontSize: 16,
color: '#333',
height: 40,
},
cancelButton: {
marginLeft: 10,
padding: 5,
},
cancelButtonText: {
fontSize: 16,
color: '#333',
},
historyContainer: {
padding: 15,
},
historyHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 10,
},
historyTitle: {
fontSize: 16,
fontWeight: 'bold',
color: '#333',
},
historyList: {
marginTop: 5,
},
historyItemContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingVertical: 10,
borderBottomWidth: 1,
borderBottomColor: '#f0f0f0',
},
historyItem: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
},
historyItemText: {
fontSize: 14,
color: '#333',
marginLeft: 10,
},
hotSearchContainer: {
padding: 15,
},
hotSearchTitle: {
fontSize: 16,
fontWeight: 'bold',
color: '#333',
marginBottom: 10,
},
hotSearchTags: {
flexDirection: 'row',
flexWrap: 'wrap',
},
hotSearchTag: {
backgroundColor: '#f5f5f5',
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 15,
marginRight: 10,
marginBottom: 10,
},
hotSearchTagText: {
fontSize: 14,
color: '#333',
},
});