首先,我主要是一名AngularJS开发人员,最近切换到React,我决定将我之前开发的一个角度webapp转换为一个反应应用程序。我对组件ExpressiveText
有一点问题,它在字符串中搜索匹配列表对象上的属性,并在其位置插入一个组件TriggerModal
,当单击时触发一个模式更多详细资料。因此,传递到ExpressiveTest
的属性为:text
,tags
和tagsProperty
。
text
是一个字符串(即"My search string"
)
tags
是一个对象数组(即[{id: 1, name: 'my', data: {...}}, {id: 2, name: 'string', data: {...}}]
tagsProperty
是要搜索为“标记”的属性的名称(即name
)
我跟着this issue一起尝试制定一个如何处理这个问题的想法。我之所以提到我来自angular的原因是因为我之前创建的组件只使用text.replace(regex, match => <trigger-modal data={tags[i]} />)
之类的东西,然后使用angulars $compile
函数来渲染文本中的组件。使用react似乎不太可能。这就是我在ExpressiveText
组件中尝试的内容:
class ExpressiveTextComponent extends React.Component {
constructor (props) {
super(props);
this.filterText = this.filterText.bind(this);
}
filterText () {
let text = this.props.text;
this.props.tags.map(tag => {
const regex = new RegExp(`(${tag[this.props.tagsProperty]})`, 'gi');
let temp = text.split(regex);
for(let i = 1; i < temp.length; i+=2){
temp[i] = <TriggerModal data={tag} label={tag[this.props.tagsProperty]} />;
}
text = temp;
});
return text;
}
render () {
return (
<div className={this.props.className}>{this.filterText()}</div>
);
}
}
这适用于第一个标签。它的问题是,一旦它转到第二个标签上的map
,text
就是一个数组。我尝试添加一个条件来检查text
是否是一个数组,但问题变成了text
数组变得嵌套而且在下一次迭代时不起作用。我非常难以理解如何处理这个问题。我也尝试dangerouslySetInnerHTML
使用text.replace(...)
,但这也不起作用,只是渲染[object Object]
来代替组件。非常感谢任何帮助或建议,我不得不说这可能是我转换到React后遇到的唯一主要问题,否则它非常简单。
编辑:由于我有一个问题要求给定输入和更多说明的预期输出,我正在寻找的是给出此输入的组件:
<ExpressiveText text="my text" tags={{id: 1, name: 'text'}} tagsProperty="name" />
会渲染
<div>my <TriggerModal label="text" data={...} /></div>
具有功能TriggerModal
组件。
答案 0 :(得分:0)
如果我理解你想要完成的事情是正确的,那么这就是实现这一目标的一种方法。如果我误解了你的问题,我很抱歉。此外,这是伪代码,我会尝试用实际代码填充它。对不起,如果这很难理解,请告诉我,我会尽力澄清
filterText () {
let text = [this.props.text];
for (let item in this.props.tags) {
//item will be something like {id: 1, name: 'text'}
let searchString = new RegExp(item.name, 'gi');
//loop through text array and see if any item matches search string regex.
while (text.some(val => val.test(searchString)) {
//if we are here, at least one item matches the regexp
//loop thru text array, and split any string by searchString, and insert <TriggerModal> in their place
for (let i = text.length-1; i >=0; i--) {
//if text[i] is string and it matches regexp, then replace with nothing
text[i].replace(searchString, "")
//insert <trigger modal>
text.splice(i, 0, <TriggerModal ... />)
}
//end of while loop - test again to see if search string still exists in test array
}
}
return text;
}
答案 1 :(得分:0)
看起来我找到了解决方案。
filterText () {
let text = this.props.text.split(' '),
replaceIndexes = [];
if(this.props.tags.length > 0) {
this.props.tags.map(tag => {
const regex = new RegExp('(' + tag[this.props.tagsProperty] + ')', 'gi');
for(let i = 0; i < text.length; i++){
if(text[i].match(regex)){
/**
* Pretty simple if its a one-word tag, search for the word and replace.
* could potentially cause some mis-matched tags but the words
* in my usecase are pretty specific, unlikely to be used in
* normal dialogue.
*/
text[i] = <TriggerModal data={tag} label={tag[this.props.tagsLabelProperty || 'name']} />;
}else{
// for tags with spaces, split them up.
let tempTag = tag[this.props.tagsProperty].split(' ');
// check for length
if(tempTag.length > 1) {
// we will be replacing at least 1 item in the array
let replaceCount = 0,
startIndex = null;
// If the first word of tempTag matches the current index, loop through the rest of the tempTag and check to see if the next words in the text array match
if(tempTag[0].toLowerCase() === text[i].toLowerCase()){
startIndex = i;
replaceCount += 1;
// loop through temp array
for (let j = 0; j < tempTag.length; j++) {
if(tempTag[j].toLowerCase() === text[i+j].toLowerCase()){
replaceCount += 1;
}
}
// Push data into replaceIndexes array to process later to prevent errors with adjusting the indexes of the text object while looping
replaceIndexes.push({
startIndex: startIndex,
replaceCount: replaceCount,
element: <TriggerModal data={tag} label={tag[this.props.tagsLabelProperty || 'name']} />
});
}
}
}
}
});
}
// Loop through each replace index object
replaceIndexes.forEach((rep, index) => {
text.splice(rep.startIndex - index, rep.replaceCount, [rep.element, ', ']);
});
// Since we stripped out spaces, we need to put them back in the places that need them.
return text.map(item => {
if(typeof item === "string"){
return item + ' ';
}
return item;
});
}
编辑:这实际上是非常错误的。我最终放弃了我自己的解决方案,转而支持this package