Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save ggeorgaras/a67f96b28ce61f75c530de1a37833f26 to your computer and use it in GitHub Desktop.
Save ggeorgaras/a67f96b28ce61f75c530de1a37833f26 to your computer and use it in GitHub Desktop.
Different approaches to dynamically rendering components with JSX

###Intro I wanted to use a facade pattern to provide a simple api for rendering job information in different forms using a Job component that received two props:

  • the job data
  • how to render the job
import React from 'react'
import Job from 'JobComponent'

const JobRenderTypes = React.createClass({ 
  render () {
    const jobData = {
      title: "Looking for help",
      employerId: "uniqueId",
    }
    return (
      <div>
        <Job data={jobData} renderAs="listing" />
        <Job data={jobData} renderAs="heading" />
        <Job data={jobData} renderAs="card" />
      </div>
    )
  }
})

A naive first approach at this Job facade looked like this:

import React from 'react'
import createJob from 'JobFactory'
import JobListing from './components/org.JobListing'
import JobHeading from './components/org.JobHeading'
import JobCard from './components/org.JobCard'

// well this is repetitive :/.... 
const renderAs = {
  listing (data) {
    return <JobListing data={data} />
  },
  heading(data) {
    return <JobHeading data={data} />
  },
  card (data) {
    return <JobCard data={data} />
  },
}

const Job = React.createClass({

  render () {
    const job = createJob(this.props.data)
    const renderFn = renderAs[this.props.renderAs]
    const content = renderFn ? renderFn(data) : ''

    return (
      <div className="Job">
        {content}
      </div>
    )
  }
})

export default Job

While returning jsx for each type of render was repetetive, I wasn't sure how to make the jsx dynamic. i.e. this doesn't work:

render() {
  const types = {
    listing: JobListing,
    heading: JobHeading,
    card: JobCard
  }
  const component = types[this.props.renderAs]
  return (<component data={jobData} />)
}

After looking into it I discovered accessing the property of an object does work. This seems to force jsx to recognize it as a variable:

render () {
  const types = {
    listing: JobListing,
    heading: JobHeading,
    card: JobCard
  }
  let render = {
   me: types[this.props.renderAs]
  }
  return (<render.me data={jobData} />)
}

Playing around a bit more yielded the discovery of a quirk of jsx. Variables beginning with non-alphabetic characters force jsx to treat it as a variable and use the variable's value rather than using the literal variable name.

render () {
  const types = {
    listing: JobListing,
    heading: JobHeading,
    card: JobCard
  }
  const component = types[this.props.renderAs] // this one will NOT work
  const $component = types[this.props.renderAs] // this one WILL
  return (
    <div>
      <component data={jobData} />
      <$component data={jobData} />
    </div
  )
}

Here is a look at what is happening when the jsx comes out the other end as plain ol' js:

render () {
  const types = {
    listing: JobListing,
    heading: JobHeading,
    card: JobCard
  }
  let component = types[this.props.renderAs] 
  let $component = types[this.props.renderAs] 
  
  // Notice it passing in "component" as a string vs $component as a variable
  return (
    React.createElement("div", null, 
  	  React.createElement("component", null), 
      React.createElement($component, null)
  ))
}

Here is the final product:

import './style'
import React from 'react'
import createJob from 'JobFactory'
import listing from './components/org.JobListing'
import heading from './components/org.JobHeading'
import details from './components/org.JobDetails'
import card from './components/org.JobCard'

const { object, string } = React.PropTypes
const DEFAULT_RENDER_TYPE = 'listing'
const renderAs = {
  listing,
  heading,
  details,
  card
}

const Job = React.createClass({

  propTypes: {
    data: object.isRequired,
    renderAs: string
  },

  /**
   * 1. Non-Alphabetic chars like '$' force JSX to recognize the variable as
   * a variable and use the variable value rather than the literal variable
   * name as a string.
   */
  render () {

    const job = createJob(this.props.data)
    const renderType = this.props.renderAs.toLowerCase() || DEFAULT_RENDER_TYPE
    const $component = renderAs[renderType] /* [1] */

    return (
      <div className="Job">
        <$component data={job} />
      </div>
    )
  }
  
})

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