基于Zustand与Router的可配置高亮组件状态架构咨询
解决方案:原子化高亮Hook + 父组件组合控制架构
核心思路是将每种高亮类型封装为独立的Hook,由父组件按需调用并传递结果给Highlight组件,组件仅负责合并状态和渲染,既符合Hooks规则,又能实现高扩展性。
1. 重构内部StoreA
让StoreA专注管理基础内容和合并后的高亮结果,不直接接收外部状态修改:
// src/storeA.js import { create } from 'zustand'; export const useStoreA = create((set) => ({ content: '', combinedHighlights: {}, setContent: (content) => set({ content }), setCombinedHighlights: (highlights) => set({ combinedHighlights: highlights }), }));
2. 封装独立高亮Hook
每种高亮类型对应一个Hook,自行处理依赖并返回计算后的高亮区间:
Red高亮(依赖StoreA自身状态)
// src/hooks/useRedHighlight.js import { useStoreA } from '../storeA'; export const useRedHighlight = () => { const triggerState = useStoreA(state => state.someTriggerState); const content = useStoreA.getState().content; const redHighlights = (() => { if (!triggerState) return []; const regex = new RegExp(triggerState, 'gi'); const matches = []; let match; while ((match = regex.exec(content)) !== null) { matches.push({ start: match.index, end: match.index + match[0].length }); } return matches; })(); return { kind: 'red', highlights: redHighlights }; };
Blue高亮(依赖外部StoreB)
// src/hooks/useBlueHighlight.js import { useStoreB } from '../storeB'; import { useStoreA } from '../storeA'; export const useBlueHighlight = () => { const blueTrigger = useStoreB(state => state.blueTrigger); const content = useStoreA.getState().content; const blueHighlights = (() => { if (!blueTrigger) return []; return [{ start: blueTrigger, end: blueTrigger + 10 }]; })(); return { kind: 'blue', highlights: blueHighlights }; };
Green高亮(依赖Next.js Router)
// src/hooks/useGreenHighlight.js import { useRouter } from 'next/router'; import { useStoreA } from '../storeA'; export const useGreenHighlight = () => { const router = useRouter(); const highlightKey = router.query.greenHighlight; const content = useStoreA.getState().content; const greenHighlights = (() => { if (!highlightKey) return []; const index = content.indexOf(highlightKey); return index === -1 ? [] : [{ start: index, end: index + highlightKey.length }]; })(); return { kind: 'green', highlights: greenHighlights }; };
3. Highlight组件实现
组件接收父组件传入的高亮数据,合并后更新StoreA,并负责渲染高亮文本:
// src/components/Highlight.jsx import { useStoreA } from '../storeA'; import { useEffect } from 'react'; export const Highlight = ({ highlights = {} }) => { const { content, combinedHighlights, setCombinedHighlights } = useStoreA(); // 合并传入的高亮数据并更新StoreA useEffect(() => { const merged = { ...combinedHighlights, ...highlights }; // 过滤空高亮结果 Object.keys(merged).forEach(key => { if (!merged[key]?.length) delete merged[key]; }); setCombinedHighlights(merged); }, [highlights, combinedHighlights, setCombinedHighlights]); // 渲染高亮文本核心逻辑 const renderHighlightedText = () => { const allRanges = Object.values(combinedHighlights).flat().sort((a, b) => a.start - b.start); if (!allRanges.length) return <span>{content}</span>; const fragments = []; let lastEnd = 0; allRanges.forEach(({ start, end }, idx) => { if (start > lastEnd) { fragments.push(<span key={`text-${idx}`}>{content.slice(lastEnd, start)}</span>); } // 匹配高亮类型并添加样式 const kind = Object.keys(combinedHighlights).find(k => combinedHighlights[k].some(r => r.start === start && r.end === end) ); fragments.push( <span key={`highlight-${idx}`} className={`highlight-${kind}`}> {content.slice(start, end)} </span> ); lastEnd = end; }); if (lastEnd < content.length) { fragments.push(<span key="text-end">{content.slice(lastEnd)}</span>); } return <>{fragments}</>; }; return <div className="highlight-container">{renderHighlightedText()}</div>; };
4. 父组件使用示例
父组件按需调用高亮Hook,动态控制启用的高亮类型:
// src/pages/example.jsx import { useState } from 'react'; import { Highlight } from '../components/Highlight'; import { useRedHighlight } from '../hooks/useRedHighlight'; import { useBlueHighlight } from '../hooks/useBlueHighlight'; import { useGreenHighlight } from '../hooks/useGreenHighlight'; import { useStoreA } from '../storeA'; export default function ExamplePage() { const { setContent } = useStoreA(); useState(() => setContent('This is a sample text for testing highlight effects.'), []); // 调用所需高亮Hook const redHighlight = useRedHighlight(); const blueHighlight = useBlueHighlight(); const greenHighlight = useGreenHighlight(); // 动态切换高亮类型 const [activeKinds, setActiveKinds] = useState(['red', 'blue']); // 收集当前启用的高亮数据 const activeHighlights = { ...(activeKinds.includes('red') && { red: redHighlight.highlights }), ...(activeKinds.includes('blue') && { blue: blueHighlight.highlights }), ...(activeKinds.includes('green') && { green: greenHighlight.highlights }), }; return ( <div> <div> <button onClick={() => setActiveKinds(['red', 'blue'])}>红+蓝高亮</button> <button onClick={() => setActiveKinds(['red', 'green'])}>红+绿高亮</button> <button onClick={() => setActiveKinds(['green'])}>仅绿高亮</button> </div> <Highlight highlights={activeHighlights} /> </div> ); }
方案优势
- 符合Hooks规则:所有Hook均在组件顶层调用,动态切换仅筛选结果,不改变调用顺序。
- 高扩展性:新增高亮类型只需封装新Hook,无需修改
Highlight组件。 - 逻辑解耦:每种高亮的依赖和计算独立,组件仅负责合并和渲染,职责单一。
- 状态自动同步:依赖变化时,对应Hook自动重新计算,父组件同步结果即可,无需手动调用
setState。
内容的提问来源于stack exchange,提问作者Yuri Sulyma
相关产品推荐
相关产品推荐

