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

React Native搜索结果自定义:如何高亮匹配关键词?

React Native 搜索关键词高亮实现方案

要实现搜索关键词高亮,核心是把包含搜索词的文本拆分为「普通文本」和「匹配关键词」两部分,分别用不同样式渲染。React Native 的 <Text> 组件支持嵌套,我们可以通过字符串拆分+正则匹配来实现。

步骤1:编写高亮处理工具函数

先写一个通用函数,接收原始文本和搜索词,返回带高亮样式的文本片段数组:

const highlightText = (text, searchTerm) => {
  if (!searchTerm.trim()) return <Text>{text}</Text>;
  
  // 转义正则特殊字符,避免搜索词含特殊字符时报错
  const escapedSearchTerm = searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
  // 大小写不敏感匹配
  const regex = new RegExp(`(${escapedSearchTerm})`, 'gi');
  
  // 拆分文本为普通片段和匹配片段
  const parts = text.split(regex);
  
  return parts.map((part, index) => {
    // 判断当前片段是否匹配搜索词(大小写不敏感)
    const isMatch = part.toLowerCase() === searchTerm.toLowerCase();
    return (
      <Text key={index} style={isMatch ? listStyles.highlightedText : {}}>
        {part}
      </Text>
    );
  });
};

步骤2:修改组件代码

替换原代码中渲染匹配文本的逻辑,同时修复原代码中 keyWord 变量作用域的问题(原代码循环会覆盖 keyWord,导致只显示最后一个匹配的 stanza):

import {
  View,
  Text,
  ScrollView,
  SafeAreaView,
  BackHandler,
} from "react-native";
import React from "react";
import { hymnsCollection } from "./data";
import styles from "../../styles/App";
import listStyles from "./Styles";
import { useNavigation } from "@react-navigation/native";

const List = ({ searchFilter }) => {
  const navigation = useNavigation();
  const data = hymnsCollection;

  // 高亮处理函数
  const highlightText = (text, searchTerm) => {
    if (!searchTerm.trim()) return <Text>{text}</Text>;
    
    const escapedSearchTerm = searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    const regex = new RegExp(`(${escapedSearchTerm})`, 'gi');
    const parts = text.split(regex);
    
    return parts.map((part, index) => {
      const isMatch = part.toLowerCase() === searchTerm.toLowerCase();
      return (
        <Text key={index} style={isMatch ? listStyles.highlightedText : {}}>
          {part}
        </Text>
      );
    });
  };

  return (
    <SafeAreaView>
      <ScrollView>
        <View style={styles.container2}>
          <View style={styles.container3}>
            {data
              .filter((val) => {
                // 优化过滤逻辑:检查stanza或id是否匹配
                const hasMatchingStanza = val.stanzas.some(e => 
                  e.toLowerCase().includes(searchFilter.toLowerCase())
                );
                const hasMatchingId = val.id.toString().includes(searchFilter);
                return hasMatchingStanza || hasMatchingId;
              })
              .map((datas) => { // 用datas.id作为key更可靠
                return searchFilter === "" ? (
                  <View key={datas.id}>
                    <Text
                      style={styles.hymnTitle}
                      onPress={() => {
                        navigation.navigate("content", {
                          info: data,
                          id: datas.id,
                        });
                      }}
                      selectable={true}
                    >
                      <Text style={styles.hymnNumber}>{`${datas.id} - `}</Text>
                      <Text style={styles.hymnTitle} selectable={true}>
                        {datas.title}
                      </Text>
                    </Text>
                  </View>
                ) : (
                  <View key={datas.id} style={listStyles.container}>
                    <Text
                      onPress={() => {
                        navigation.navigate("content", {
                          info: data,
                          id: datas.id,
                        });
                      }}
                    >
                      <Text style={listStyles.hymnNumber}>{`${datas.id} - `}</Text>
                      <Text style={listStyles.hymnTitle} selectable={true}>
                        {highlightText(datas.title, searchFilter)} {"\n"}
                      </Text>
                      {/* 渲染所有匹配的stanza,而不是最后一个 */}
                      {datas.stanzas.map((stanza, stanzaIndex) => {
                        if (stanza.toLowerCase().includes(searchFilter.toLowerCase())) {
                          return (
                            <Text key={stanzaIndex} style={listStyles.hymncontents}>
                              {highlightText(stanza, searchFilter)} {"\n"}
                            </Text>
                          );
                        }
                        return null;
                      })}
                    </Text>
                  </View>
                );
              })}
          </View>
        </View>
      </ScrollView>
    </SafeAreaView>
  );
};

export default List;

步骤3:添加高亮样式

在你的 listStyles 文件中添加高亮样式,比如:

const listStyles = StyleSheet.create({
  // ... 原有样式
  highlightedText: {
    backgroundColor: 'yellow',
    fontWeight: 'bold',
  },
});

关键优化点

  • 修复了原代码中 keyWord 变量被循环覆盖的问题,现在会渲染所有匹配的 stanza
  • 使用 datas.id 作为列表项的 key,比索引更稳定
  • 优化了过滤逻辑,用 some 替代 map,性能更好
  • 处理了正则特殊字符的转义,避免搜索词含 .、* 等字符时出错
  • 同时支持标题和stanza中的关键词高亮

内容的提问来源于stack exchange,提问作者Wizard Chiboy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 16:40:06