You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 08:44:58