メインコンテンツまでスキップ

🎨 ブロックオーバーライドガイド

✨ 概要

CreateOrganizationコンポーネントは、ブロックオーバーライドを通じて強力なカスタマイズ機能を提供します。このパターンでは、共有のフォーム状態処理を維持しながら、フォーム構造と UI コンポーネントをカスタマイズできます。

🚀 基本的なオーバーライドパターン

function Example() {
const defaultData = {
applicationUserName: '',
applicationUserEmail: '',
applicationUserPhoneNumber: '',
companyName: '',
postcode: '',
cityTownVillage: '',
townStreetBuilding: '',
representivePersonName: '',
representativePhoneNumber: '',
companyUrl: '',
additionalInformation: '',
establishmentDate: '',
prefecture: '',
capital: '',
numberOfEmployees: '',
userAgreement: '',
privacyPolicy: '',
};

const [data, setData] = React.useState(defaultData);

return (
<CreateOrganization data={data} onDataChange={setData}>
{({ defaultBlocks, defaultBlockOrder }) => ({
blocks: {
...defaultBlocks,
customNotice: (
<div
style={{
width: '100%',
border: '1px solid #cddcff',
borderRadius: 8,
padding: 12,
background: '#eef4ff',
fontSize: 14,
}}
>
ブロックオーバーライドパターンで追加されたカスタム通知
</div>
),
},
blockOrder: ['customNotice', ...defaultBlockOrder],
})}
</CreateOrganization>
);
}

🔧 完全なカスタマイズ例

CreateOrganization を使った、実際の深いネストのブロックオーバーライド例です:

ライブエディター
function Example() {
  const defaultData = {
    applicationUserName: '',
    applicationUserEmail: '',
    applicationUserPhoneNumber: '',
    companyName: '',
    postcode: '',
    cityTownVillage: '',
    townStreetBuilding: '',
    representivePersonName: '',
    representativePhoneNumber: '',
    companyUrl: '',
    additionalInformation: '',
    establishmentDate: '',
    prefecture: '',
    capital: '',
    numberOfEmployees: '',
    userAgreement: '',
    privacyPolicy: '',
  };

  const [data, setData] = React.useState(defaultData);

  return (
    <CreateOrganization
      data={data}
      onDataChange={setData}
      termsOfUseUrl="/terms-of-use"
      privacyAgreementUrl="/privacy-policy"
      onSearchAddressClick={() => {
        setData((current) => ({ ...current }));
      }}
      onSubmit={(event) => {
        event.preventDefault();
        setData((current) => ({ ...current }));
      }}
    >
      {({ defaultBlocks, defaultBlockOrder }) => ({
        blocks: {
          ...defaultBlocks,
          title: {
            ...defaultBlocks.title,
            props: {
              ...defaultBlocks.title.props,
              children: '組織への申請',
            },
          },
          description: {
            ...defaultBlocks.description,
            props: {
              ...defaultBlocks.description.props,
              children: '以下のフォームを使って新しい組織を登録します。',
            },
          },
          form: {
            ...defaultBlocks.form,
            props: {
              ...defaultBlocks.form.props,
              children({ defaultBlocks: formBlocks, defaultBlockOrder: formBlockOrder }) {
                return {
                  blocks: {
                    ...formBlocks,
                    accountHolder: {
                      ...formBlocks.accountHolder,
                      props: {
                        ...formBlocks.accountHolder.props,
                        children({ defaultBlocks: accountHolderBlocks, defaultBlockOrder: accountHolderBlockOrder }) {
                          return {
                            blocks: {
                              ...accountHolderBlocks,
                              sectionTitle: {
                                ...accountHolderBlocks.sectionTitle,
                                props: {
                                  ...accountHolderBlocks.sectionTitle.props,
                                  children: 'アカウント保有者',
                                },
                              },
                              emailField: {
                                ...accountHolderBlocks.emailField,
                                props: {
                                  ...accountHolderBlocks.emailField.props,
                                  label: 'メールアドレス',
                                },
                              },
                            },
                            blockOrder: accountHolderBlockOrder,
                          };
                        },
                      },
                    },
                    companyInformation: {
                      ...formBlocks.companyInformation,
                      props: {
                        ...formBlocks.companyInformation.props,
                        children({
                          defaultBlocks: companyInformationBlocks,
                          defaultBlockOrder: companyInformationBlockOrder,
                        }) {
                          return {
                            blocks: {
                              ...companyInformationBlocks,
                              sectionTitle: {
                                ...companyInformationBlocks.sectionTitle,
                                props: {
                                  ...companyInformationBlocks.sectionTitle.props,
                                  children: '会社情報',
                                },
                              },
                              companyAddress: {
                                ...companyInformationBlocks.companyAddress,
                                props: {
                                  ...companyInformationBlocks.companyAddress.props,
                                  children({
                                    defaultBlocks: companyAddressBlocks,
                                    defaultBlockOrder: companyAddressBlockOrder,
                                  }) {
                                    return {
                                      blocks: {
                                        ...companyAddressBlocks,
                                        locationTitle: {
                                          ...companyAddressBlocks.locationTitle,
                                          props: {
                                            ...companyAddressBlocks.locationTitle.props,
                                            children: '本社所在地',
                                          },
                                        },
                                        postCode: {
                                          ...companyAddressBlocks.postCode,
                                          props: {
                                            ...companyAddressBlocks.postCode.props,
                                            searchAddressLabel: '住所を検索',
                                          },
                                        },
                                        representativeContact: {
                                          ...companyAddressBlocks.representativeContact,
                                          props: {
                                            ...companyAddressBlocks.representativeContact.props,
                                            placeholder: '代表者の電話番号',
                                          },
                                        },
                                      },
                                      blockOrder: companyAddressBlockOrder,
                                    };
                                  },
                                },
                              },
                              companyCapital: {
                                ...companyInformationBlocks.companyCapital,
                                props: {
                                  ...companyInformationBlocks.companyCapital.props,
                                  label: '払込資本',
                                },
                              },
                              companySize: {
                                ...companyInformationBlocks.companySize,
                                props: {
                                  ...companyInformationBlocks.companySize.props,
                                  label: 'チーム規模',
                                },
                              },
                            },
                            blockOrder: companyInformationBlockOrder,
                          };
                        },
                      },
                    },
                    compliance: {
                      ...formBlocks.compliance,
                      props: {
                        ...formBlocks.compliance.props,
                        children({ defaultBlocks: complianceBlocks, defaultBlockOrder: complianceBlockOrder }) {
                          return {
                            blocks: {
                              ...complianceBlocks,
                              checkUserAgreement: {
                                ...complianceBlocks.checkUserAgreement,
                                props: {
                                  ...complianceBlocks.checkUserAgreement.props,
                                  label: (
                                    <>
                                      <strong>利用規約</strong>に同意します。
                                    </>
                                  ),
                                },
                              },
                              checkPrivacyAgreement: {
                                ...complianceBlocks.checkPrivacyAgreement,
                                props: {
                                  ...complianceBlocks.checkPrivacyAgreement.props,
                                  label: (
                                    <>
                                      <strong>プライバシーポリシー</strong>に同意します。
                                    </>
                                  ),
                                },
                              },
                            },
                            blockOrder: complianceBlockOrder,
                          };
                        },
                      },
                    },
                    submitButton: {
                      ...formBlocks.submitButton,
                      props: {
                        ...formBlocks.submitButton.props,
                        children: '組織を送信',
                      },
                    },
                  },
                  blockOrder: formBlockOrder,
                };
              },
            },
          },
        },
        blockOrder: defaultBlockOrder,
      })}
    </CreateOrganization>
  );
}
結果
Loading...

🎯 主要なオーバーライド概念

1. ブロックオーバーライド構造

  • blocks: コンポーネント定義を含むオブジェクト
  • blockOrder: レンダリング順序を制御する配列
  • defaultBlocks: 元のコンポーネント構造
  • defaultBlockOrder: 元のレンダリング順序

2. ネストされたオーバーライド

form: {
...defaultBlocks.form,
props: {
...defaultBlocks.form.props,
children({defaultBlocks, defaultBlockOrder}) {
// ネストされたブロックオーバーライドをここで実行
}
}
}

3. カスタムコンポーネント

デフォルトブロックを完全にカスタムコンポーネントに置き換えます:

customField: <MyCustomComponent {...props} />

📐 ベストプラクティス

  1. 常にデフォルトの props を展開する ことで既存機能を維持する
  2. 意味のあるブロック名を使う ことで保守性を高める
  3. blockOrder の一貫性を維持する(意図的に順序を変える場合を除く)
  4. カスタムフィールドに適切なバリデーションを実装する
  5. 読み込み状態とエラー状態を適切に処理する

🚀 利点

  • 🎨 完全な UI 制御: フォームのあらゆる側面をカスタマイズできる
  • 🔧 柔軟なバリデーション: カスタムバリデーションロジックを実装できる
  • 📱 レスポンシブデザイン: レスポンシブな動作を維持できる
  • 🔄 状態管理: フォーム状態を完全に制御できる

プロのヒント: 小さなオーバーライドから始めて、徐々に複雑さを増やしましょう。ブロックオーバーライドパターンは強力ですが、props の展開とコンポーネント構造に細心の注意が必要です。