Shindig: React Data Component

Chet Corcos
2 min readNov 24, 2015

--

I’ve noticed that some people use React like an object oriented framework. User interface components have this physicality that tends to make us think that way, but React is actually very functional. In fact, components are just functions with props and children as arguments. And if you stick to good functional programming patterns and limit the side-effects of your components, things start to build on top of each other very nicely.

Every view in my app starts with a 100% pure component — pure meaning there are no side-effects and you get the same output for the same input every time. Here’s a simple example of the user item component in a list of users.

UserItem = createView
mixins: [React.addons.PureRenderMixin]
render: ->
cond @props.loading,
=>
div
className: 'user-item loading'
=>
div
className: 'user-item'
onClick: @props.onClick
div
className: 'prof-pic'
style:
backgroundImage: "url(#{@props.user.img})"
div
className: 'name'
@props.user.name

Notice how this component has no side-effects and there’s no MeteorData mixin either. This component is far more reusable than a component that fetches data itself.

Any data fetching going on in my app is delegated to a single React component that passes on the data as props to the pure views. The Data component accepts a data store from ccorcos:any-store, a query, and a render function. This pattern limits all side-effects in my entire UI to this single component type.

Data = createView
mixins: [React.addons.PureRenderMixin]
getInitialState: ->
{data, fetch, clear, watch} = @props.store.get(@props.query)
componentWillMount: ->
if @state.data or not @state.fetch
@setState({loading: false})
@listen()
else
@fetch()
listen: ->
@listener = @state.watch (nextState) =>
@setState(nextState)
fetch: ->
@listener?.stop()
@setState({loading:true})
@state.fetch (nextState) =>
if @isMounted()
nextState.loading = false
@setState(nextState)
@listen()
componentWillUnmount: ->
@listener?.stop()
@state.clear()
render: ->
@props.render
data: @state.data
fetch: @state.fetch
loading: @state.loading

This component allows me to tie data fetching to the React render tree. Every view that needs to fetch data has a Meta- version that performs all the data fetching, passing the data to a pure component as props.

MetaUserItem = createView
mixins: [React.addons.PureRenderMixin]
renderData: ({data, loading}) ->
UserItem
user: data
loading: loading
onClick: @props.push
className: @props.className
render: ->
Data
key: @props.userId
query: {id: @props.userId}
store: UserStore
render: @renderData

You must provide a unique key to the Data component ensure that a new component is mounted whenever there is a new query. Otherwise the get-fetch-watch-clear sequence will mess up and you’ll end up with a memory leak in your subscriptions.

This component is also included in the ccorcos:react-ui Meteor package among other things.

So that basically wraps it up in terms of the tools I used to build Shindig. Next I thought I’d elaborate on some of the programming patterns and best practices I’ve discovered.

--

--