perf(Company): 优化渲染性能和 API 请求
- StockQuoteCard: 添加 memo 包装减少重渲染 - Company/index: componentProps 使用 useMemo 缓存 - useCompanyEvents: 页面浏览事件只触发一次,避免重复追踪 - useCompanyData: 自选股状态改用单股票查询接口,减少数据传输 - CompanyHeader: inputCode 状态下移到 SearchActions,减少父组件重渲染 - CompanyHeader: 移除重复环境光效果,由全局 AmbientGlow 统一处理 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -137,25 +137,29 @@ const StockInfoDisplay = memo<{
|
||||
StockInfoDisplay.displayName = 'StockInfoDisplay';
|
||||
|
||||
/**
|
||||
* 搜索操作区组件
|
||||
* 搜索操作区组件(状态自管理,减少父组件重渲染)
|
||||
*/
|
||||
const SearchActions = memo<{
|
||||
inputCode: string;
|
||||
onInputChange: (value: string) => void;
|
||||
onSearch: () => void;
|
||||
onSelect: (value: string) => void;
|
||||
stockCode: string;
|
||||
onStockChange: (value: string) => void;
|
||||
isInWatchlist: boolean;
|
||||
watchlistLoading: boolean;
|
||||
onWatchlistToggle: () => void;
|
||||
}>(({
|
||||
inputCode,
|
||||
onInputChange,
|
||||
onSearch,
|
||||
onSelect,
|
||||
stockCode,
|
||||
onStockChange,
|
||||
isInWatchlist,
|
||||
watchlistLoading,
|
||||
onWatchlistToggle,
|
||||
}) => {
|
||||
// 输入状态自管理(避免父组件重渲染)
|
||||
const [inputCode, setInputCode] = useState(stockCode);
|
||||
|
||||
// 同步外部 stockCode 变化
|
||||
React.useEffect(() => {
|
||||
setInputCode(stockCode);
|
||||
}, [stockCode]);
|
||||
|
||||
// 股票搜索 Hook
|
||||
const searchHook = useStockSearch({
|
||||
limit: 10,
|
||||
@@ -190,18 +194,28 @@ const SearchActions = memo<{
|
||||
}));
|
||||
}, [searchResults]);
|
||||
|
||||
// 处理搜索按钮点击
|
||||
const handleSearch = useCallback(() => {
|
||||
if (inputCode && inputCode !== stockCode) {
|
||||
onStockChange(inputCode);
|
||||
}
|
||||
}, [inputCode, stockCode, onStockChange]);
|
||||
|
||||
// 选中股票
|
||||
const handleSelect = useCallback((value: string) => {
|
||||
clearSearch();
|
||||
onSelect(value);
|
||||
}, [clearSearch, onSelect]);
|
||||
setInputCode(value);
|
||||
if (value !== stockCode) {
|
||||
onStockChange(value);
|
||||
}
|
||||
}, [clearSearch, stockCode, onStockChange]);
|
||||
|
||||
// 键盘事件
|
||||
const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
|
||||
if (e.key === 'Enter') {
|
||||
onSearch();
|
||||
handleSearch();
|
||||
}
|
||||
}, [onSearch]);
|
||||
}, [handleSearch]);
|
||||
|
||||
return (
|
||||
<HStack spacing={3}>
|
||||
@@ -241,7 +255,7 @@ const SearchActions = memo<{
|
||||
options={stockOptions}
|
||||
onSearch={doSearch}
|
||||
onSelect={handleSelect}
|
||||
onChange={onInputChange}
|
||||
onChange={setInputCode}
|
||||
placeholder="输入代码、名称或拼音"
|
||||
style={{ width: 240 }}
|
||||
dropdownStyle={{
|
||||
@@ -271,7 +285,7 @@ const SearchActions = memo<{
|
||||
size="md"
|
||||
h="42px"
|
||||
px={5}
|
||||
onClick={onSearch}
|
||||
onClick={handleSearch}
|
||||
leftIcon={<Icon as={Search} boxSize={4} />}
|
||||
fontWeight="bold"
|
||||
borderRadius="10px"
|
||||
@@ -335,28 +349,6 @@ const CompanyHeader: React.FC<CompanyHeaderProps> = memo(({
|
||||
onStockChange,
|
||||
onWatchlistToggle,
|
||||
}) => {
|
||||
const [inputCode, setInputCode] = useState(stockCode);
|
||||
|
||||
// 处理搜索
|
||||
const handleSearch = useCallback(() => {
|
||||
if (inputCode && inputCode !== stockCode) {
|
||||
onStockChange(inputCode);
|
||||
}
|
||||
}, [inputCode, stockCode, onStockChange]);
|
||||
|
||||
// 处理选中
|
||||
const handleSelect = useCallback((value: string) => {
|
||||
setInputCode(value);
|
||||
if (value !== stockCode) {
|
||||
onStockChange(value);
|
||||
}
|
||||
}, [stockCode, onStockChange]);
|
||||
|
||||
// 同步 stockCode 变化
|
||||
React.useEffect(() => {
|
||||
setInputCode(stockCode);
|
||||
}, [stockCode]);
|
||||
|
||||
return (
|
||||
<Box
|
||||
position="relative"
|
||||
@@ -368,20 +360,7 @@ const CompanyHeader: React.FC<CompanyHeaderProps> = memo(({
|
||||
backdropFilter={FUI_GLASS.blur.md}
|
||||
overflow="hidden"
|
||||
>
|
||||
{/* 环境光效果 - James Turrell 风格 */}
|
||||
<Box
|
||||
position="absolute"
|
||||
top={0}
|
||||
left={0}
|
||||
right={0}
|
||||
bottom={0}
|
||||
pointerEvents="none"
|
||||
bg={`radial-gradient(ellipse 80% 50% at 20% 40%, ${FUI_COLORS.ambient.warm}, transparent),
|
||||
radial-gradient(ellipse 60% 40% at 80% 60%, ${FUI_COLORS.ambient.cool}, transparent)`}
|
||||
opacity={0.6}
|
||||
/>
|
||||
|
||||
{/* 顶部发光线 */}
|
||||
{/* 顶部发光线(环境光效果由全局 AmbientGlow 提供) */}
|
||||
<Box
|
||||
position="absolute"
|
||||
top={0}
|
||||
@@ -413,10 +392,8 @@ const CompanyHeader: React.FC<CompanyHeaderProps> = memo(({
|
||||
|
||||
{/* 右侧:搜索和操作 */}
|
||||
<SearchActions
|
||||
inputCode={inputCode}
|
||||
onInputChange={setInputCode}
|
||||
onSearch={handleSearch}
|
||||
onSelect={handleSelect}
|
||||
stockCode={stockCode}
|
||||
onStockChange={onStockChange}
|
||||
isInWatchlist={isInWatchlist}
|
||||
watchlistLoading={watchlistLoading}
|
||||
onWatchlistToggle={onWatchlistToggle}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* - 公司信息(成立、注册资本、所在地、官网、简介)
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { memo } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
@@ -540,4 +540,4 @@ const StockQuoteCard: React.FC<StockQuoteCardProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default StockQuoteCard;
|
||||
export default memo(StockQuoteCard);
|
||||
|
||||
Reference in New Issue
Block a user