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 therest_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 aconnect
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)
- Exposing hooks from which a sync layer could replay data received via
- Potential alternatives include:
- 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