javascript - 如何使用 JSX 突出显示字符串中的匹配项?
问题描述
我有一个自定义的自动完成功能,所以当你输入时,它会根据输入值显示一个建议列表。在列表中,我想将与输入值相同的字符加粗。
因此,如果我有一个建议列表:“alligator”、“lima”、“lime”,并且我输入了“li”,那么建议将如下所示:
- 鳄鱼_ _
- 立马_
- 我_
map
我的 jsx 文件中有这个简单的:
<ul>
{matches.map(function(match, idx){
let re = new RegExp(value, 'g');
let str = match.replace(re, '<b>'+ value +'</b>');
return <li key={idx}>{str}</li>
})}
</ul>
value
输入值在哪里。它显示列表,但以这种字符串格式
- al<b>li</b>鳄鱼
- <b>李</b>妈
- <b>我</b>我
不知道如何使用 React。我想过使用dangerouslyinnerhtml
或类似的东西,但我认为这是不得已而为之的事情。如果可能的话,我想避免这种情况。
这是我的自动完成组件:
class Autocomplete extends Component{
constructor(props){
super(props);
this.state = {
value: '',
matches: [],
showMatches: false
}
}
searchListing(){
api.call {
that.setState({
showMatches: true,
matches: a
});
})
}
}
handleOnChangeInput(e){
let value = e.target.value;
this.setState({ value: value})
if(value !== ''){
this.searchListing(e);
}else{
// console.log("value", e.target.value);
this.setState({
showMatches: false,
matches: []
})
}
}
render(){
let matches = this.state.matches;
let value = this.state.value;
let matchesHtml;
if(this.state.showMatches){
matchesHtml = <ul>
{matches.map(function(match, idx){
let re = new RegExp(value, 'g');
let str = match.replace(re, '<b>'+ value +'</b>');
return <li key={idx} dangerouslySetInnerHTML={{__html: str}}></li>
})}
</ul>
}
return(
<div>
<input placeholder="type a name" onChange={this.handleOnChangeInput}/>
{matchesHtml}
</div>
);
}
}
解决方案
编写自己的突出显示代码可能会导致一个兔子洞。在我的回答中,我假设只有简单的文本(字符串中没有 HTML,没有字符集边缘情况)和有效的非转义RegExp
模式字符串。
您可以构建一个新数组,而不是构建一个新字符串,您可以在其中放置 JSX。
render() { // No need to wrap list items in an extra element! return [ // Don't forget the keys :) <li key="A">First item</li>, <li key="B">Second item</li>, <li key="C">Third item</li>, ]; }
背后的逻辑
作为一个简单的概念证明,我们可以使用以下逻辑:
const defaultHighlight = s => <em>{s}</em>;
// Needed if the target includes ambiguous characters that are valid regex operators.
const escapeRegex = v => v.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
/**
* Case insensitive highlight which keeps the source casing.
* @param {string} source text
* @param {string} target to highlight within the source text
* @param {Function} callback to define how to highlight the text
* @returns {Array}
*/
const highlightWord = (source, target, callback) => {
const res = [];
if (!source) return res;
if (!target) return source;
const regex = new RegExp(escapeRegex(target), 'gi');
let lastOffset = 0;
// Uses replace callback, but not its return value
source.replace(regex, (val, offset) => {
// Push both the last part of the string, and the new part with the highlight
res.push(
source.substr(lastOffset, offset - lastOffset),
// Replace the string with JSX or anything.
(callback || defaultHighlight)(val)
);
lastOffset = offset + val.length;
});
// Push the last non-highlighted string
res.push(source.substr(lastOffset));
return res;
};
/**
* React component that wraps our `highlightWord` util.
*/
const Highlight = ({ source, target, children }) =>
highlightWord(source, target, children);
const TEXT = 'This is a test.';
const Example = () => (
<div>
<div>Nothing: "<Highlight />"</div>
<div>No target: "<Highlight source={TEXT} />"</div>
<div>Default 'test': "<Highlight source={TEXT} target="test" />"</div>
<div>Multiple custom with 't':
"<Highlight source={TEXT} target="t">
{s => <span className="highlight">{s}</span>}
</Highlight>"
</div>
<div>Ambiguous target '.':
"<Highlight source={TEXT} target=".">
{s => <span className="highlight">{s}</span>}
</Highlight>"
</div>
</div>
);
// Render it
ReactDOM.render(
<Example />,
document.getElementById("react")
);
.highlight {
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
不需要在dangerouslySetInnerHTML
这里使用。
此highlightWord
函数可以采用任何函数来包装匹配的字符串。
highlight(match, value) // default to `s => <em>{s}</em>`
// or
highlight(match, value, s => <span className="highlight">{s}</span>);
我正在根据Stack Overflow上的另一个答案进行最小的正则表达式字符串转义。
Highlight
组件_
如图所示,我们可以创建一个组件,使其“更具反应性”!
/**
* React component that wraps our `highlightWord` util.
*/
const Highlight = ({ source, target, children }) =>
highlightWord(source, target, children);
Highlight.propTypes = {
source: PropTypes.string,
target: PropTypes.string,
children: PropTypes.func,
};
Highlight.defaultProps = {
source: null,
target: null,
children: null,
};
export default Highlight;
它使用render prop,因此您必须将渲染更改为:
<ul>
{matches.map((match, idx) => (
<li key={idx}>
<Highlight source={match} target={value}>
{s => <strong>{s}</strong>}
</Highlight>
</li>
))}
</ul>
推荐阅读
- android - 构建不同风味的颤振
- html - 从右到左反转svg
- javascript - javascript无法初始化全局变量
- php - Woocommerce 运输核心调整
- angular - gapi.auth2.signin() 成功回调未在移动设备上调用
- c# - 如何在 ViewModel 中使用我的条目的“.isFocused”属性?
- c++ - 如何在这段 C++ 代码中编写部分字母重复将起作用?
- windows - Golang docker 容器无法在 Windows Home 上启动
- postgresql - Postgres 的问题:“pg_ctl:无法启动服务器”
- javascript - Gatsby / Docker 导致 MaxListenersExceededWarning