4

I have the following JSON contract which gets passed down to certain components that basically iterate over the format and render the data inside of p elements. However, I'd like the ability to set a custom HTML element instead of the default p element used.

What are some good techniques for achieving this?

For example:

// from
const data = [{ text: 'hello' }, { text: 'world' }];
// to 
const data = [{ text: 'hello', element: 'span' }, { text: 'world', element: 'h1' }];

Basically iterates over each item in the array and uses their custom HTML element to render the text. I know one can achieve this by using React's non JSX syntax, however, these components are quite complex, so I'd ideally like to use JSX. Any thoughts? Maybe there's a way to combine the two (use non JSX for the custom elements and within them use JSX?

Detuned
  • 3,652
  • 4
  • 27
  • 54

3 Answers3

4

Map use of React.createElement() function inside a map like React.createElement(item.element, null, item.text)

class App extends React.Component {
  constructor(){
    super();
    this.state= {
      data : [{ text: 'hello', element: 'span' }, { text: 'world', element: 'h1' }]
    }
  }
  render() {
    
    return (
      <div>
        {this.state.data.map(function(item) {
          return (React.createElement(item.element, null, item.text)) 
        })}
      </div>
    )
  }
}

ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.min.js"></script>
<div id="app"></div>
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
1

One way that I can think of is to have a switch statement such as, it's maybe a bit too much if you have lots though, but I can't think of another way at the moment without having the element return the span component directly which I'm not sure is possible from backend.

_renderElementsFromJSON = (data) => {
    var elements = [];

    for(var i = 0; i < data.length; i++) {
        switch(data[i].element){
            case "span":
                elements.push(<span key={i}>{data[i].text}</span>);
                break;
            case "h1":
                elements.push(<h1 key={i}>{data[i].h1}</span>);
                break;
            //etc
        }
    }
    return elements;
}
Martin Dawson
  • 7,455
  • 6
  • 49
  • 92
  • Yeah, definitely an option, but to handle all cases would be difficult, as you mentioned. I've using the non-JSX syntax which works great, but curious if there are cleaner JSX solutions. – Detuned Oct 18 '16 at 17:38
0

You can use JSX syntax if you create a variable whose name starts with a capital letter:

class App extends React.Component {
  constructor(){
    super();
    this.state= {
      data : [{ text: 'hello', element: 'span' }, { text: 'world', element: 'h1' }]
    }
  }
  render() {
    return (
      <div>
        {this.state.data.map((item) => {
          const Element = item.element; // Note that this "Element" variable must begin with capital letter.
          return <Element>{item.text}</Element>;
        })}
      </div>
    )
  }
}

ReactDOM.render(<App/>, document.getElementById('app'));

This could be helpful: ReactJS component names must begin with capital letters?

Nacho Justicia Ramos
  • 8,313
  • 1
  • 16
  • 26