3

I'd like to make all the numbers in a string red and then render it with React. Here's what I'm trying to do (I've made an app using create-react-app and replaced the contents of App.js with my own):

import React, { Component } from 'react';
import './App.css';

class App extends Component {
  render() {
    const str = 'foo123bar';

    const strColor = 
      str.replace(/\d+/, match => <span style={{color: 'red'}}> {match} </span> );

    return (
      <div className="App">
        {strColor}
      </div>
    );
  }
}

export default App;

As a result only the line foo[object Object]bar was rendered in the viewport.

So how should inline styling be added to JSX?

1
  • 1
    I think the question that you're really trying to answer is how to replace parts of a string, with a component (in this case a <span> tag with styling. This issue is documented in the react github issues page, note that there are many options without requiring you to dangerously set your inner HTML: github.com/facebook/react/issues/3386 Commented Feb 6, 2019 at 20:57

3 Answers 3

4

I was able to solve this by using 'dangerouslySetInnerHTML'.

class App extends React.Component {
  render() {
      let str = 'foo123bar';

      const strColor = str.replace(/\d+/, match => `<span style="color: red">${match} </span>` );

    return (
        <div className="App"
         dangerouslySetInnerHTML={{__html: 
          strColor}}>
      </div>
);

} }

Sign up to request clarification or add additional context in comments.

1 Comment

OP's question is mainly asking about adding inline styling - I think it's worth noting your change to the inline style definition in your answer. Also note that there are many options without requiring dangerously setting the inner HTML: github.com/facebook/react/issues/3386
3

"So how should inline styling be added to JSX?"

To answer to your stated question, your line:

const strColor = str.replace(/\d+/, match => <span style={{color: 'red'}}> {match} </span> );

is returning a string - so the object statement style={{color: 'red'}}> will not be escaped.

Add the inline styling with a string definition instead, reference the double quotes and removed curly braces:
<span style="color: red"> {match} </span>

You can add more styles by separating the key: value pairs with commas:
<span style="color: red, text-decoration: underline"> {match} </span>


Note that this will not work
The question that you're really trying to answer is how to replace parts of a string, with a component (in this case a <span> tag with styling. This issue is documented in the react github issues page, note that there are many options without requiring you to dangerously set your inner HTML as noted in several previous answers: https://github.com/facebook/react/issues/3386

6 Comments

Thanks! So what's the easiest way to do that?
@EugeneBarsky There's a Github repo mentioned in the issue thread that you can pull into your project via Git. Here is the link to the repo: github.com/iansinnott/react-string-replace. This repo provides a function called reactStringReplace() that will handle your use case. If you scroll down through the README documentation there is an example given called "More realistic example" that addresses your needs directly. Hope this helps!
Thank you! Finally, I modified one of the solutions from the discussion page, and it seems to work perfectly for my project.
glad to hear it!
Also probably faster / easier / more legible than importing an external library. Good idea!
|
3

You can't insert HTML into a string for it to be rendered in React, this exists as a protection from XSS.

What you can do in a case like this is something like:

const str = 'foo123bar';

const coloredStr = str.match(/\d+/);
const [before, after] = str.split(/\d+/)

return (
  <div className="App">
    {before}
    {coloredStr && coloredStr[0] && 
       <span style="color: red">{coloredStr[0]}</span>
    }
    {after}
  </div>
);

For a more complex example you will need more complex logic. E.g. multiple parts can be styled - you can find all the matches and the non matching parts and put them in a list in the right order with an indicator should you use the span or not. Something like:

list.map((elem) => elem.isColored ? <span style="color: red">{elem.value}</span> : elem.value)

EDIT

As mentioned in the comments, here is an implementation for multiple elements:

const str = 'foo123bar456baz897ban';
let strCopy = str;
const list = [];

while(strCopy) {
    const text = strCopy.split(/\d+/, 1)[0];
    list.push(text);
    const match = strCopy.match(/\d+/);
    if (!match) {
        break;
    }
    list.push(match[0]);
    strCopy = strCopy.substring(match.index + match[0].length);
}

return (
  <div className="App">
    {list.map((elem, index) => index % 2 === 0
         ? elem
         : <span style="color: red">{elem}</span>
    )}
  </div>
);

5 Comments

I thought about splitting, but what if I have str = 'foo123bar456baz897ban'?
As I've mentioned, that will be a bit tricky, I'll try to code the example first and will edit my answer accordingly :)
Here it is. I didn't test the render part, just the loop, but it should be ok. Feel free to comment if I've missed something. :)
Thanks! If I'm not mistaken, I've used a similar approach from a discussion thread on github. github.com/facebook/react/issues/3386#issuecomment-404464020
Yeah, the idea is the same, but the code in that comment is much nicer. :+1: I wanted to keep mine as simple as possible (without using the regex g flag and all) :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.