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

React.js中如何给搜索结果匹配关键词单独设置样式并解决相关报错

问题1:span标签渲染为[object Object]

根因:String.prototype.replace 仅支持传入字符串作为替换值,你传入的<span>是JSX对象,被自动转为字符串后就会输出[object Object]。
解决方法:将文本按搜索词切割为数组,遍历数组为匹配到的片段包裹样式标签,返回React可直接渲染的混合文本/元素数组,参考函数实现:

const highlightMatch = (text, query) => {
  // 搜索词为空时直接返回处理后的原文本
  if (!query.trim()) return text.replace(/- /g, "").replace(/\./g, "");
  // 用括号包裹正则匹配项,split时会保留匹配项本身
  const re = new RegExp(`(${query})`, "ig");
  const parts = text.replace(/- /g, "").replace(/\./g, "").split(re);
  // 过滤空片段后遍历生成内容
  return parts.filter(Boolean).map((part, idx) => {
    return part.toLowerCase() === query.toLowerCase() 
      ? <span key={idx} className="boldFont">{part}</span>
      : part;
  });
};

问题2:列表key警告

根因:两处遍历生成元素列表时未添加唯一key属性:

  1. 遍历searchResults生成<li>标签时未加key
  2. 上述高亮函数中生成的span元素作为数组项也需要加key(上面的函数实现已经补充了key)

修改后完整代码

import React, { Fragment } from "react";
import CancelIcon from "@material-ui/icons/Cancel";
import CallMadeIcon from "@material-ui/icons/CallMade";

export default function SearchBar(props) {
  // 把高亮函数提出来避免每次渲染重复声明
  const highlightMatch = (text, query) => {
    if (!query.trim()) return text.replace(/- /g, "").replace(/\./g, "");
    const re = new RegExp(`(${query})`, "ig");
    const parts = text.replace(/- /g, "").replace(/\./g, "").split(re);
    return parts.filter(Boolean).map((part, idx) => {
      return part.toLowerCase() === query.toLowerCase() 
        ? <span key={idx} className="boldFont">{part}</span>
        : part;
    });
  };

  return (
    <Fragment>
      <div className="search">
        <div
          className="search-input-container"
          ref={props.searchInputContainerRef}
        >
          <label htmlFor="search-input"></label>
          <input
            type="text"
            id="search-input"
            placeholder={props.placeholder}
            onChange={props.handleSetSearchTerm}
            value={props.searchTerm}
            ref={props.searchInputRef}
            onFocus={props.blurAllOnSearch}
            onBlur={props.blurAllOnSearch}
            onKeyUp={props.handleFilter}
          />
          {props.searchTerm === "" ? (
            <button
              ref={props.searchIconVectorRef}
              type="button"
              className="search-icon-btn"
              onClick={props.focusSearchInput}
            ></button>
          ) : (
            <CancelIcon
              className="clear-btn"
              type="button"
              onClick={props.clearSearchInput}
            />
          )}
          <button className="go-search-btn">Go</button>
        </div>
        {props.searchResults.length !== 0 && (
          <div className="search-results">
            {/* 给li添加key,优先用搜索结果的唯一业务id比如value.id,没有的话再用index */}
            {props.searchResults.slice(0, 15).map((value, index) => {
              let bolded = highlightMatch(value.title, props.searchTerm);
              return (
                <li key={value.id || index} className="data-item">
                  <a
                    className="result"
                    href={value.link}
                    target="_blank"
                    rel="noreferrer"
                  >
                    {bolded}
                  </a>
                  <CallMadeIcon className="call-made-icon" />
                </li>
              );
            })}
          </div>
        )}
      </div>
    </Fragment>
  );
}

后续你可以直接通过CSS修改.boldFont类的样式实现自定义高亮效果。

内容的提问来源于stack exchange,提问作者Klem Lloyd Mwenya

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 08:06:04