2

I have a block of HTML content that is being pulled in via a graphql query. Within this text string, I have 'variables' in the form %variable%. I need to replace the variables with dynamically generated content. The easiest way would be to have the variable replaced with the output from a React component. The reason for wanting to use a Component in this case, is because I need to get data from another source to replace the variable, and the data will change based on different inputs, so I can't just replace string for string.

If I do content.replace("%variable%,<Component />) I get:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vel lobortis massa. Fusce ac enim ullamcorper, vulputate diam ut, consequat diam. Duis ante odio, suscipit a rhoncus eget, sollicitudin vel nibh. Proin ut urna sed diam dictum commodo sed nec felis. In hac habitasse platea dictumst. Vivamus varius nulla mi, [Object object] convallis lorem aliquam ac. Quisque congue nibh sit amet nisl ultrices gravida. Integer ac lobortis nibh. Donec interdum placerat nibh, eget pharetra tellus rutrum nec. In varius arcu eu luctus posuere. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean porttitor orci et eros auctor, at porta dui bibendum. Donec sed consequat felis.

So, %variable is being replaced by [Object object] - not what I was hoping...

I've looked at some other responses to similar queries, but can't really see a solution that would apply in this instance.

Is this possible?

2
  • replace function returns string. That's why it prints [Object object]. How to do this? Interesting question :) Commented May 27, 2020 at 13:55
  • > I've looked at some other responses to similar queries Commented May 27, 2020 at 13:58

2 Answers 2

1

You can use a combination of ReactDOMServer.renderToStaticMarkup and dangerouslySetInnerHTML to tackle your situation.

working code demo

---Edited based on comment--- For multiple variable replacements, maintain a mapping of variables & components as shown in the code snippet as well as the demo link

Code snippet

const Comp1 = () => (
  <div>
    dynamic component 1 <br />
  </div>
);
const Comp2 = () => (
  <div>
    dynamic component 2 <br />
  </div>
);
const Comp3 = () => (
  <div>
    dynamic component 3 <br />
  </div>
);

const variableToCompMapping = {
  "%variable1%": Comp1,
  "%variable2%": Comp2,
  "%variable3%": Comp3
};

export default function App() {
  const [content, setContent] = useState("");

  useEffect(() => {
    setContent(`
    lorem %variable1% ipsum 
    lorem %variable2% ipsum 
    lorem %variable3% ipsum 
    `);
  }, []);

  const renderContent = () => {
    let result = content;
    for (let dynamicVar in variableToCompMapping) {
      const compAsHtml = ReactDOMServer.renderToStaticMarkup(
        variableToCompMapping[dynamicVar]()
      );
      result = result.replace(dynamicVar, compAsHtml);
    }
    return result;
  };

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <div
        className="content"
        dangerouslySetInnerHTML={{
          __html: renderContent()
        }}
      />
    </div>
  );
}

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

5 Comments

This does what I need. However, if I have multiple variables (variable1,variable2,etc.) is there a way to have multiple dangerouslySetInnerHTML applied to the string? I've just been trying this, but can only seem to get one variable replaced.
ok - updated the answer as well as the codesandbox link (use the same link - refresh the page..) ... hope all good now...
Thanks! This is really valuable and helps my (very new) understanding of React
awesome :) ... you can see if you can consider to accept the answer.. thanks.
Is there a way that I can pass props to the components that are defined in const variableToCompMapping = { "%variable1%": Comp1, "%variable2%": Comp2, "%variable3%": Comp3 };
1

Here is a possible approach. split the content by the template %variable%. Iterate over the outcome, use map to render each piece. For each iteration, render also <Component />.

Like this:

import React from "react";
import "./styles.css";

const getContent = () =>
  "Lorem ipsum dolor sit amet %variable% Proin ut urna sed diam %variable% per inceptos himenaeos";

const Component = () => (
  <span style={{ color: "red" }}>I'm actually Component</span>
);

export default function App() {
  const [content, setContent] = React.useState([]);
  React.useEffect(() => {
    setContent(getContent().split("%variable%"));
  }, []);
  return (
    <div className="App">
      {content.map((child, i) => (
        <>
          {child}
          {i < content.length && <Component />}
        </>
      ))}
    </div>
  );
}

Update

In order to support different variable, use split with regex instead of plain string. The second part is to hold a mapper between the variable name and the equivalent Component and in render time check if the current item is a variable (startsWith('%')) render the equivalent Component, otherwise render the item (which is the next piece from the original string)

import React from "react";
import "./styles.css";

const getContent = () =>
  "Lorem ipsum dolor sit amet %variable% Proin ut urna sed diam %variable1% per inceptos himenaeos";

const Component = () => (
  <span style={{ color: "red" }}>I'm actually Component </span>
);

const Component1 = () => (
  <span style={{ color: "green" }}>I'm actually Component </span>
);

const mapper = {
  "%variable%": <Component />,
  "%variable1%": <Component1 />
};

export default function App() {
  const [content, setContent] = React.useState([]);
  React.useEffect(() => {
    setContent(getContent().split(/(%.*?% )/g));
  }, []);
  return (
    <div className="App">
      {content.map(child => (
        <>{child.startsWith("%") ? mapper[child.trim()] : child}</>
      ))}
    </div>
  );
}

https://codesandbox.io/s/gracious-panini-yylug?file=/src/App.js

2 Comments

That's a possibility, but I have instances where there are different variables in the text (e.g. variable1, variable2, etc.) and each variable needs to call a different component.
My pleasure 😊. If it answers your question, please accept it so it will help to other people.

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.