GutenbergのregisterStoreで注意すべき点

gutenberg WordPress

より高度なGutenbergプラグインを作成する場合は registerStore を使用した状態管理が必須です。

@wordpress/data | Block Editor Handbook | WordPress Developer Resources

実装方法によっては状態を変更してもすぐに画面に反映されないため注意が必要です。

例 

例えば flag の状態が false の時に ON、true の時に OFF と表示されるボタンを PluginMoreMenuItem に追加します。

store/constant.js

const PLUGIN_NAME = 'PLUGIN_TEST';
export const STORE_NAME = PLUGIN_NAME;
export const SET_FLAG = '@@' + PLUGIN_NAME + '/SET_FLAG';

store/actions.js

import { SET_FLAG } from './constant';

export function setFlag( flag ) {
	return {
		type: SET_FLAG,
		flag,
	};
}

store/reducers.js

import { SET_FLAG } from './constant';

export default ( state = {
	flag: false,
}, action ) => {
	switch ( action.type ) {
		case SET_FLAG:
			state.flag = action.flag;
			return state;
	}
	return state;
}

store/selectors.js

export function getFlag( state ) {
	return state.flag;
}

component/more-menu.js

const { Fragment } = wp.element;
const { PluginMoreMenuItem } = wp.editPost;
const { withSelect, withDispatch } = wp.data;
const { compose } = wp.compose;

import { STORE_NAME } from '../store/constant';

export function MoreMenu( { flag, toggleFlag } ) {
	return <Fragment>
		<PluginMoreMenuItem
			onClick={ toggleFlag }
		>
			{ flag ? 'OFF' : 'ON' }
		</PluginMoreMenuItem>
	</Fragment>;
}

export default compose(
	withSelect( ( select ) => {
		return {
			flag: select( STORE_NAME ).getFlag(),
		};
	} ),
	withDispatch( ( dispatch, { flag } ) => {
		return {
			toggleFlag: () => dispatch( STORE_NAME ).setFlag( ! flag ),
		};
	} ),
)( MoreMenu );

store/index.js

const { registerStore } = wp.data;
import reducer from './reducer';
import * as selectors from './selectors';
import * as actions from './actions';
import { STORE_NAME } from './constant';

registerStore( STORE_NAME, { reducer, selectors, actions } );

index.js

const registerPlugin = wp.plugins.registerPlugin;

import './store';
import MoreMenu from './components/more-menu';

registerPlugin( 'my-test-plugin', {
	render: () => <MoreMenu/>,
} );

上のような実装の場合、内部の状態 flag が更新されても flag によるボタンのラベル ON、OFF の切り替えはすぐには反映されません。

右上の歯車マークを押してメニューを一度消したり、サイドバーのパネルを切り替えたりしてからもう一度確認したりすると反映されます。

解決方法

Store を使用したアプリをすでに多く作ってきた人は上のプログラムの欠陥にすぐ気づくと思いますが、reducer に修正すべき箇所があります。

具体的には引数の state の flag を変更して返却している箇所です。

JavaScript における object の比較は参照の比較になるので、引数の state と返り値は同じ値という判定になり、変化がないと判断されてしまいます。

これを解決することで正しい動作になります。

修正

store/reducers.js

import { SET_FLAG } from './constant';

export default ( state = {
	flag: false,
}, action ) => {
	switch ( action.type ) {
		case SET_FLAG:
			const newState = Object.assign( {}, state );
			newState.flag = action.flag;
			return newState;
	}
	return state;
}

参照が同じにならないように新しく object を作成して、それを返却するようにします。

すぐに反映されるようになりました。

コメント

タイトルとURLをコピーしました