###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
Good work, been looking for something like this for dynamic element rendering through Firebase. Thanks!