Skip to content

Instantly share code, notes, and snippets.

@jeffhandley
Last active July 20, 2016 13:45
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jeffhandley/0ebf0fd7f04b3b2613a72b2fe1054e45 to your computer and use it in GitHub Desktop.
Save jeffhandley/0ebf0fd7f04b3b2613a72b2fe1054e45 to your computer and use it in GitHub Desktop.
Is there a better way to build this React component other than using ReactDOMServer.renderToStaticMarkup and dangerouslySetInnerHTML?
// Returns a translated form of "Welcome back, <strong>Jeff</strong>. It's good to see you again."
// There are several messages that can be supplied and they can have different sentence structures
// Regardless of the message, the name needs to be wrapped in a <strong> tag
import React from 'react';
import ReactDOMServer from 'react-dom/server';
export default React.createClass({
displayName: 'Greeting',
propTypes: {
formatMessage: React.PropTypes.func.isRequired,
message: React.PropTypes.string.isRequired,
name: React.PropTypes.string.isRequired
},
render() {
const nameHtml = ReactDOMServer.renderToStaticMarkup(
<strong>{ this.props.name }</strong>
);
return (
<span dangerouslySetInnerHTML={{ __html:
this.props.formatMessage(this.props.message, { nameHtml })
}} />
);
}
});
@amanda-mitchell
Copy link

I recently built a somewhat similar control without the use of dangerouslySetInnerHTML. Here are some of the major differences:

  • Rather than a format function, I used a string with a well-defined token pattern (%TOKEN%), which allows for more than one placeholder. Upon getting a new prop value, I split the string up between normal text and placeholders.
  • I defined a placeholder component with a name property on it.
  • In the render method of my formatted component, I iterate over the tokens of my format string and search the component's children for a placeholder of the same name and replace the token. I then return from the render method an array that contains spans for the text pieces of the format strings mixed with the placeholders that were found.

The usage looks like this:

<Formatter value="Welcome back, %NAME%">
  <Formatter.Value name="NAME"><strong>{this.props.name}</strong></Formatter.Value>
</Formatter>

The code is currently internal to my employer, but it's relatively simple and I could see about open sourcing it if it sounds like it would solve your problem.

@jeffhandley
Copy link
Author

@david-mitchell Sounds very interesting! So you end up creating an array of nodes where some nodes render as pure text and other nodes render as the nodes that were passed in as children?

Do you feel the string splitting is solid enough to be better than using dangerouslySetInnerHTML? Despite "dangerously," I think what I'm doing is actually safe.

@amanda-mitchell
Copy link

That's correct. Given the scope of what you're doing, I think your method is fine; the component I created is more useful when you want to have more complicated UI in the placeholder slots. For example, if you wanted to do a localizable, Outlook-style recurrence picker of the form, "Every (second) (Tuesday) of every | 2 | months", where instead of my bad ascii art, you had interactive controls, the component that I built would be ideal.

@jeffhandley
Copy link
Author

Ooh, interactive controls as the placeholders. That's cool, @david-mitchell!!

I'll keep that in mind. Splitting the string doesn't seem too terrible to gain that power.

Thanks!

@amanda-mitchell
Copy link

It seems that GitHub doesn't send emails for mentions on gists…if you've got any more questions/ideas, feel free to find me on Twitter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment