Skip to content

Instantly share code, notes, and snippets.

@taion
Last active January 24, 2017 17:14
Show Gist options
  • Save taion/66b6ce17b601568d7ad3321e3dda8b8d to your computer and use it in GitHub Desktop.
Save taion/66b6ce17b601568d7ad3321e3dda8b8d to your computer and use it in GitHub Desktop.
React Router data fetching
// Define your components like:
class MyComponent extends React.Component {
static fetchData = (params) => {
// return an action here.
};
/* ... */
}
function fetchComponentData(component, store, params) {
if (component.fetchData) {
store.dispatch(component.fetchData(params));
};
}
export default store => ({
createRouterContext: (child, { components, params }) => {
for (const component of components) {
if (!component) {
continue;
}
if (typeof component === 'object') {
Object.keys(component).forEach(key => {
fetchComponentData(component[key], store, params);
});
continue;
}
fetchComponentData(component, store, params);
}
return child;
},
});
// Define your routes like:
const myRoute = (
<Route
component={myComponent}
fetchData={params => /* action */}
/>
);
export default store => ({
createRouterContext: (child, { routes, params }) => {
routes
.map(route => route.fetchData)
.filter(fetchData => fetchData)
.forEach(fetchData => store.dispatch(fetchData(params)));
return child;
},
});
ReactDOM.render(
<Router
history={history}
routes={routes}
render={applyRouterMiddleware(useFetchData(store))}
/>,
container
);
@esmevane
Copy link

Discovered a bug with the above. Here's a more complete resolution:

import React from 'react'

class DispatcherContext extends React.Component {
  _fetchData() {
    const { action, dispatch } = this.props

    dispatch(action())
  }

  // We trigger a fetchData call the first time we're loaded, every time.
  // 
  componentDidMount() { this._fetchData() }

  // If this component is passed different children at any point, we want to
  // trigger another fetchData call.
  // 
  // Just putting a blanket update here tends to lead to infinite loops.
  // 
  componentDidUpdate(previous) {
    const previousName = previous.children.type.displayName
    const currentName = this.props.children.type.displayName

    if (previousName !== currentName) { this._fetchData() }
  }

  // We're a bare wrapper around a component with fetchable actions.
  // 
  render() { return this.props.children }
}

export const dispatcherContext = store => {
  // We want to wrap every component with a fetchData static property. I.E.,
  // fetchables.
  // 
  const renderRouteComponent = child => {
    if (child.type.fetchData) {
      return(
        <DispatcherContext dispatch={ store.dispatch }
                           action={ child.type.fetchData }>
          { child }
        </DispatcherContext>
      )
    }

    return child
  }

  return { renderRouteComponent }
}

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