Skip to content

Instantly share code, notes, and snippets.

@aduth
Created August 19, 2017 07:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aduth/e5dd6327963475e9f580b8ece523ffa0 to your computer and use it in GitHub Desktop.
Save aduth/e5dd6327963475e9f580b8ece523ffa0 to your computer and use it in GitHub Desktop.

Related: #902

Warning: Highly incomplete, experimental, potentially throwaway idea.

This pull request seeks to introduce a new withApiData higher-order component to provide a simple interface for fetching, creating, updating, or deleting data from the REST API. In its current form it is very much incomplete, and in some ways flawed, but presented for feedback before continuing.

Example:

function MyPost( { post } ) {
	if ( post.isLoading ) {
		return <div>Loading...</div>;
	}

	return <div>{ post.value.title.rendered }</div>;
}

export default withApiData( ( props, endpoint ) => ( {
	post: endpoint`/wp/v2/posts/${ props.postId }`
} ) )( MyPost );

Implementation notes:

withApiData behaves similar to, and in some cases complements, React-Redux's connect function. It accepts props which can either be passed from the parent component, or composed from another higher-order component (e.g. connect), and returns an object of propName -> endpoint result.

The endpoint function is a tagged template literal which finds the corresponding route details from the REST API schema. This is then used to determine which actions can be performed for a given route path (can fetch? can save? etc). Currently only a basic fetch operation is implemented.

Equivalent ES5 for endpoint function call above: endpoint( [ '/wp/v2/posts/' ], props.postId )

There is some inspiration here from Heroku's react-refetch project.

Future enhancements include:

  • Deduplicating and caching responses
  • Full suite of entity handling (post.isLoading, post.isError, post.isFulfilled, post.isSaving, post.create(), post.update(), post.delete())

Downsides include:

  • As can be seen in the included LastRevision conversion, manually writing API paths is troublesome because it is not easy to know the rest_base of a custom post type (see also: #1276)
  • Is not integrated into Redux state. I leaned heavily on general reusability and simple APIs here instead (simple in sense of not manually juggling lifecycle of a request). Of course, the withApiData can inherit props from a connect call, but otherwise does not expose received data into editor's state.
    • Potential alternatives include:
      • Exposing hooks from which a sync layer could replay data received via withApiData into Redux state.
      • Opting for a Redux-first approach. Might come at the cost of reusability. I'd considered some helpers like a "WordPress API" store enhancer which could be applied to any Redux store, or a middleware which infers data needs from dispatched action, or a reducer utility which cuts down on boilerplate for API data (manages loading states, pagination, queries, etc)
  • Tagged template literal may be an unfamiliar concept, uglier to implement in ES5, and maybe overkill if route keys can instead be converted to a JS regular expression (without named captures) and applied to a simple string
  • Duplicates or otherwise doesn't (can't?) use the Backbone API client
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment