Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API: Replace all withAPIData usage and deprecate the HoC #8584

Merged
merged 3 commits into from
Aug 6, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 7 additions & 10 deletions core-blocks/latest-posts/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {
Spinner,
ToggleControl,
Toolbar,
withAPIData,
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { decodeEntities } from '@wordpress/html-entities';
Expand All @@ -26,7 +25,7 @@ import {
BlockAlignmentToolbar,
BlockControls,
} from '@wordpress/editor';
import { addQueryArgs } from '@wordpress/url';
import { withSelect } from '@wordpress/data';

/**
* Internal dependencies
Expand All @@ -50,8 +49,7 @@ class LatestPostsEdit extends Component {
}

render() {
const latestPosts = this.props.latestPosts.data;
const { attributes, categoriesList, setAttributes } = this.props;
const { attributes, categoriesList, setAttributes, latestPosts } = this.props;
const { displayPostDate, align, postLayout, columns, order, orderBy, categories, postsToShow } = attributes;

const inspectorControls = (
Expand All @@ -60,7 +58,7 @@ class LatestPostsEdit extends Component {
<QueryControls
{ ...{ order, orderBy } }
numberOfItems={ postsToShow }
categoriesList={ get( categoriesList, [ 'data' ], {} ) }
categoriesList={ categoriesList }
selectedCategoryId={ categories }
onOrderChange={ ( value ) => setAttributes( { order: value } ) }
onOrderByChange={ ( value ) => setAttributes( { orderBy: value } ) }
Expand Down Expand Up @@ -158,21 +156,20 @@ class LatestPostsEdit extends Component {
}
}

export default withAPIData( ( props ) => {
export default withSelect( ( select, props ) => {
const { postsToShow, order, orderBy, categories } = props.attributes;
const { getEntityRecords } = select( 'core' );
const latestPostsQuery = pickBy( {
categories,
order,
orderby: orderBy,
per_page: postsToShow,
_fields: [ 'date_gmt', 'link', 'title' ],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are going to fetch more data, is that expected?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, with a central store like we do right now, we can't fetch a small part of objects, we fetch the whole objects or we don't. Because the redux state always expects the full object.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could, but it is super complex :)

}, ( value ) => ! isUndefined( value ) );
const categoriesListQuery = {
per_page: 100,
_fields: [ 'id', 'name', 'parent' ],
};
return {
latestPosts: addQueryArgs( '/wp/v2/posts', latestPostsQuery ),
categoriesList: addQueryArgs( '/wp/v2/categories', categoriesListQuery ),
latestPosts: getEntityRecords( 'postType', 'post', latestPostsQuery ),
categoriesList: getEntityRecords( 'taxonomy', 'category', categoriesListQuery ),
};
} )( LatestPostsEdit );
34 changes: 14 additions & 20 deletions docs/blocks/creating-dynamic-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,26 @@ The following code example shows how to create the latest post block dynamic blo

var el = wp.element.createElement,
registerBlockType = wp.blocks.registerBlockType,
withAPIData = wp.components.withAPIData;
withSelect = wp.data.withSelect;

registerBlockType( 'my-plugin/latest-post', {
title: 'Latest Post',
icon: 'megaphone',
category: 'widgets',

edit: withAPIData( function() {
edit: withSelect( function( select ) {
return {
posts: '/wp/v2/posts?per_page=1'
posts: select( 'core' ).getEntityRecords( 'postType', 'post' )
};
} )( function( props ) {
if ( ! props.posts.data ) {
return "loading !";
}
if ( props.posts.data.length === 0 ) {
if ( props.posts && props.posts.length === 0 ) {
return "No posts";
}
var className = props.className;
var post = props.posts.data[ 0 ];
var post = props.posts[ 0 ];

return el(
'a',
'a',
{ className: className, href: post.link },
post.title.rendered
);
Expand All @@ -50,26 +47,23 @@ registerBlockType( 'my-plugin/latest-post', {
// myblock.js

const { registerBlockType } = wp.blocks;
const { withAPIData } = wp.components;
const { withSelect } = wp.data;

registerBlockType( 'my-plugin/latest-post', {
title: 'Latest Post',
icon: 'megaphone',
category: 'widgets',

edit: withAPIData( () => {
edit: withSelect( ( select ) => {
return {
posts: '/wp/v2/posts?per_page=1'
posts: select( 'core' ).getEntityRecords( 'postType', 'post' )
};
} )( ( { posts, className } ) => {
if ( ! posts.data ) {
return "loading !";
}
if ( posts.data.length === 0 ) {
if ( posts && posts.length === 0 ) {
return "No posts";
}
var post = posts.data[ 0 ];
var post = posts[ 0 ];

return <a className={ className } href={ post.link }>
{ post.title.rendered }
</a>;
Expand Down Expand Up @@ -119,7 +113,7 @@ There are a few things to notice:

## Live rendering in Gutenberg editor

Gutenberg 2.8 added the [`<ServerSideRender>`](https://github.com/WordPress/gutenberg/tree/master/packages/components/src/server-side-render) block which enables all the rendering to take place on the server using PHP rather than in JavaScript. Server-side render is meant as a fallback; client-side rendering in JavaScript is the preferred implementation.
Gutenberg 2.8 added the [`<ServerSideRender>`](https://github.com/WordPress/gutenberg/tree/master/packages/components/src/server-side-render) block which enables all the rendering to take place on the server using PHP rather than in JavaScript. Server-side render is meant as a fallback; client-side rendering in JavaScript is the preferred implementation.

{% codetabs %}
{% ES5 %}
Expand Down
4 changes: 4 additions & 0 deletions docs/reference/deprecated.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
Gutenberg's deprecation policy is intended to support backwards-compatibility for two minor releases, when possible. The current deprecations are listed below and are grouped by _the version at which they will be removed completely_. If your plugin depends on these behaviors, you must update to the recommended alternative before the noted version.

## 3.7.0

- `wp.components.withAPIData` has been removed. Please use the Core Data module or `wp.apiFetch` directly instead.

## 3.6.0

- `wp.editor.editorMediaUpload` has been removed. Please use `wp.editor.mediaUpload` instead.
Expand Down
7 changes: 7 additions & 0 deletions packages/components/src/higher-order/with-api-data/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { mapValues, reduce, forEach, noop } from 'lodash';
import { Component } from '@wordpress/element';
import { createHigherOrderComponent } from '@wordpress/compose';
import isShallowEqual from '@wordpress/is-shallow-equal';
import deprecated from '@wordpress/deprecated';

/**
* Internal dependencies
Expand All @@ -17,6 +18,12 @@ import request, { getCachedResponse } from './request';
import { getRoute } from './routes';

export default ( mapPropsToData ) => createHigherOrderComponent( ( WrappedComponent ) => {
deprecated( 'wp.components.withAPIData', {
version: 3.7,
plugin: 'Gutenberg',
alternative: 'the Core Data Module or wp.apiFetch directly',
} );

class APIDataComponent extends Component {
constructor( props, context ) {
super( ...arguments );
Expand Down
17 changes: 17 additions & 0 deletions packages/core-data/src/entities.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const defaultEntities = [

export const kinds = [
{ name: 'postType', loadEntities: loadPostTypeEntities },
{ name: 'taxonomy', loadEntities: loadTaxonomyEntities },
];

/**
Expand All @@ -40,6 +41,22 @@ async function loadPostTypeEntities() {
} );
}

/**
* Returns the list of the taxonomies entities.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: The function returns a promise. The promise resolves to a list of the taxonomies entities.

Aside: This is probably one of the only things I dislike about async / await, that it becomes very non-obvious that it will always return a Promise (I think even if the function has no return statement? My uncertainty reenforces my point 😄 ).

*
* @return {Promise} Entities promise
*/
async function loadTaxonomyEntities() {
const taxonomies = await apiFetch( { path: '/wp/v2/taxonomies?context=edit' } );
return map( taxonomies, ( taxonomy, name ) => {
return {
kind: 'taxonomy',
baseURL: '/wp/v2/' + taxonomy.rest_base,
name,
};
} );
}

/**
* Returns the entity's getter method name given its kind and name.
*
Expand Down
41 changes: 16 additions & 25 deletions packages/editor/src/components/page-attributes/parent.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ import { get } from 'lodash';
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { TreeSelect, withAPIData } from '@wordpress/components';
import { TreeSelect } from '@wordpress/components';
import { compose } from '@wordpress/compose';
import { withSelect, withDispatch } from '@wordpress/data';
import { addQueryArgs } from '@wordpress/url';

/**
* Internal dependencies
Expand All @@ -20,7 +19,7 @@ import { buildTermsTree } from '../../utils/terms';
export function PageAttributesParent( { parent, postType, items, onUpdateParent } ) {
const isHierarchical = get( postType, [ 'hierarchical' ], false );
const parentPageLabel = get( postType, [ 'labels', 'parent_item_colon' ] );
const pageItems = get( items, [ 'data' ], [] );
const pageItems = items || [];
if ( ! isHierarchical || ! parentPageLabel || ! pageItems.length ) {
return null;
}
Expand All @@ -42,14 +41,24 @@ export function PageAttributesParent( { parent, postType, items, onUpdateParent
}

const applyWithSelect = withSelect( ( select ) => {
const { getPostType } = select( 'core' );
const { getPostType, getEntityRecords } = select( 'core' );
const { getCurrentPostId, getEditedPostAttribute } = select( 'core/editor' );
const postTypeSlug = getEditedPostAttribute( 'type' );
const postType = getPostType( postTypeSlug );
const postId = getCurrentPostId();
const isHierarchical = get( postType, [ 'hierarchical' ], false );
const query = {
per_page: -1,
exclude: postId,
parent_exclude: postId,
orderby: 'menu_order',
order: 'asc',
};

return {
postId: getCurrentPostId(),
parent: getEditedPostAttribute( 'parent' ),
postType: getPostType( postTypeSlug ),
postTypeSlug,
items: isHierarchical ? getEntityRecords( 'postType', postTypeSlug, query ) : [],
postType,
};
} );

Expand All @@ -62,25 +71,7 @@ const applyWithDispatch = withDispatch( ( dispatch ) => {
};
} );

const applyWithAPIDataItems = withAPIData( ( { postType, postId } ) => {
const isHierarchical = get( postType, [ 'hierarchical' ], false );
const restBase = get( postType, [ 'rest_base' ], false );
const query = {
context: 'edit',
per_page: -1,
exclude: postId,
parent_exclude: postId,
_fields: [ 'id', 'parent', 'title' ],
orderby: 'menu_order',
order: 'asc',
};
return isHierarchical && restBase ?
{ items: addQueryArgs( `/wp/v2/${ restBase }`, query ) } :
{};
} );

export default compose( [
applyWithSelect,
applyWithDispatch,
applyWithAPIDataItems,
] )( PageAttributesParent );