Reputation: 63
today I faced a problem with highlighting matching parenthesis in React. This is example text:
(my text (is here and) I want (to highlight (something here)))
And I want it to look like in code editor, I mean: image attached
I tried to use react-process-string
:
processString([
{
regex: /\(|\)/g,
fn: (key, result) => this.colorBracket(key, result[0])
}
])(value);
colorBracket = (key, result) => {
const { usedColors } = this.state;
const bracketColors = ['red', 'green', 'yellow', 'blue', 'purple'];
let newColor = '';
if (result === '(') {
newColor =
usedColors.length
? bracketColors[usedColors.length]
: bracketColors[0];
if (!usedColors.includes(newColor)) {
this.setState({ usedColors: [...usedColors, newColor] });
}
} else {
newColor = usedColors.length
? usedColors[usedColors.length - 1]
: bracketColors[0];
if (usedColors.length) {
this.setState({ usedColors: usedColors.filter(e => e !== newColor) });
}
}
return <span style={{ color: newColor }}>{result}</span>;
};
but I faced problem with react maximum update depth
.
Is it possible to do it more simple, without updating state and so on?
Upvotes: 2
Views: 773
Reputation: 168841
Sure thing, it's not really hard at all once you know the right tools.
Here's a CodeSandbox example I whipped up, and a snippet of the same below (slightly adjusted for Stack Overflow's ancient Babel version).
The idea is:
string.split
's regexp mode to split the string into fragments that are either brackets or aren'tThe colors in this example are only 3 levels deep, but you could easily add more, or make the colorization loop over N colors using the modulus operator.
function BracketHighlighter({ text }) {
const children = React.useMemo(() => {
const out = [];
let level = 0;
text.split(/([()])/).forEach((bit) => {
if (bit === "(") {
level++;
out.push(<span className={"l" + level}>{bit}</span>);
} else if (bit === ")") {
out.push(<span className={"l" + level}>{bit}</span>);
level--;
} else {
out.push(bit);
}
});
return out;
}, [text]);
return React.createElement(React.Fragment, {}, ...children);
}
function App() {
const [text, setText] = React.useState(
"(my text (is here and) I want (to highlight (something here)))"
);
return (
<div className="App">
<input value={text} onChange={(e) => setText(e.target.value)} />
<br />
<BracketHighlighter text={text} />
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
.l1 {
background-color: orange;
}
.l2 {
background-color: lightgreen;
}
.l3 {
background-color: cyan;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Upvotes: 3