Notion APIでulを作りたいが、空の配列にpushした際の挙動がわからない。
Q&A
Closed
Notion APIとNext.jsでブログ構築をしているのですが、
Notion API周りの処理はnotion-blog-nextjsを元にしており、
liの親要素にulやolを作りたく、こちらの記事を参考に組んでみましたが、olの出力がうまくいかず、1から書き直した結果が後述のソースコードになります。
本題
組み直したところ空の配列にpushしている時点で挙動があやしく、原因がわかりません。
最後に添付のNotionブロックを処理するとき、
各ブロックを処理して配列に入れているのでulList.push(block);
をすると1回目のループで最初のリストのオブジェクトが配列に入る認識でしたが、実際にはすべてのbulleted_list_item
が入ってしまいました。
なぜすべて入ってしまうのでしょうか。どのように記述すれば治せるのでしょうか。
長くなり申し訳ありませんが教えていただけますと幸いです。
実際のソースコード
notionBlocks.jsx
import Head from 'next/head';
import Link from 'next/link';
import { Fragment } from 'react';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { tomorrow } from 'react-syntax-highlighter/dist/cjs/styles/prism';
import styles from '@style/components/post/notionBlocks.module.scss';
import Image from 'next/image';
export const Text = ({ text }) => {
if (!text) {
return null;
}
return text.map((value, index) => {
const {
annotations: { bold, code, color, italic, strikethrough, underline },
text,
} = value;
// テキストの装飾に関連するクラスを必要に応じて生成
const annotationClassArray = [
bold && styles.bold,
code && styles.inlineCode,
italic && styles.italic,
strikethrough && styles.strikethrough,
underline && styles.underline,
];
const annotationClassString = annotationClassArray.filter((val) => val !== false).join(' ');
// テキストの色の装飾に関連するオブジェクトを生成
const styleObject = color.includes('default')
? {}
: color.endsWith('_background')
? { textDecorationColor: `var(--u-background-color-${color.replace('_background', '')})` }
: { color: `var(--u-text-color-${color})` };
if (code) {
if ('textDecorationColor' in styleObject) {
return (
<code key={index} className={annotationClassString}>
<mark style={styleObject}>{text.content}</mark>
</code>
);
} else {
return (
<code key={index} className={annotationClassString} style={styleObject}>
{text.content}
</code>
);
}
} else if (annotationClassString !== '' || !!Object.keys(styleObject).length) {
if ('textDecorationColor' in styleObject) {
return (
<mark key={index} className={annotationClassString} style={styleObject}>
{text.content}
</mark>
);
} else {
return (
<span key={index} className={annotationClassString} style={styleObject}>
{text.content}
</span>
);
}
} else {
return <Fragment key={index}>{text.content}</Fragment>;
}
});
};
export const createListWrap = (listBlocksArr) => {
const listWrapClass = styles.listWrapper;
switch (listBlocksArr[0].type) {
case 'bulleted_list_item':
return (
<ul key={listBlocksArr[0].id} className={`${listWrapClass} ${listWrapClass}_ul`}>
{listBlocksArr.map((listBlock) => renderBlock(listBlock))}
</ul>
);
case 'numbered_list_item':
return (
<ol key={listBlocksArr[0].id} className={`${listWrapClass} ${listWrapClass}_ol`}>
{listBlocksArr.map((listBlock) => renderBlock(listBlock))}
</ol>
);
}
};
const renderBlock = (block) => {
const { type, id } = block;
const value = block[type];
switch (type) {
case 'paragraph':
if (value.rich_text.length === 0) {
return <br key={id} />;
} else {
return (
<p key={id}>
<Text text={value.rich_text} />
</p>
);
}
case 'heading_1':
return (
<h1 className={styles.heading_1}>
<Text text={value.rich_text} />
</h1>
);
case 'heading_2':
return (
<h2 className={styles.heading_2}>
<Text text={value.rich_text} />
</h2>
);
case 'heading_3':
return (
<h3 className={styles.heading_3}>
<Text text={value.rich_text} />
</h3>
);
case 'bulleted_list_item':
case 'numbered_list_item':
return (
<li key={id} className={styles.listItem}>
<Text text={value.rich_text} />
</li>
);
case 'to_do':
return (
<div>
<label htmlFor={id}>
<input type="checkbox" id={id} defaultChecked={value.checked} />{' '}
<Text text={value.rich_text} />
</label>
</div>
);
case 'toggle':
return (
<details>
<summary>
<Text text={value.rich_text} />
</summary>
{value.children?.map((block) => (
<Fragment key={block.id}>{renderBlock(block)}</Fragment>
))}
</details>
);
case 'child_page':
return <p>{value.title}</p>;
case 'image':
const src = value.type === 'external' ? value.external.url : value.file.url;
const caption = value.caption ? value.caption[0]?.plain_text : '';
return (
<figure className={styles.image}>
<img src={src} alt={caption} loading="lazy" decoding="async" />
{/* <Image src={src} alt={caption} loading="lazy" decoding="async" /> */}
{caption && <figcaption className={styles.imageCaption}>{caption}</figcaption>}
</figure>
);
case 'divider':
return <hr key={id} />;
case 'quote':
return (
<blockquote key={id} className={styles.quote}>
{value.rich_text[0].plain_text}
</blockquote>
);
case 'code':
return (
<SyntaxHighlighter
key={id}
language={value.language}
style={tomorrow}
customStyle={{ fontFamily: 'var(--font-mono)' }}
showLineNumbers
className={styles.codeBlock}
>
{value.rich_text[0].plain_text}
</SyntaxHighlighter>
);
case 'callout':
let emojiClass, noteTitle;
switch (value.icon.emoji) {
case '🚨':
emojiClass = styles.noteAlert;
break;
case '⚠️':
emojiClass = styles.noteWarn;
noteTitle = '注意';
break;
case '✅':
emojiClass = styles.noteCheck;
break;
default:
emojiClass = styles.noteInfo;
break;
}
return (
<div key={id} className={styles.note}>
<p className={`${styles.noteTitle} ${emojiClass}`} aria-label={noteTitle}>
{noteTitle}
</p>
<p className={styles.noteContents}>
<Text text={value.rich_text} />
</p>
</div>
);
case 'file':
const src_file = value.type === 'external' ? value.external.url : value.file.url;
const splitSourceArray = src_file.split('/');
const lastElementInArray = splitSourceArray[splitSourceArray.length - 1];
const caption_file = value.caption ? value.caption[0]?.plain_text : '';
return (
<figure>
<div>
📎{' '}
<Link href={src_file} passHref>
{lastElementInArray.split('?')[0]}
</Link>
</div>
{caption_file && <figcaption>{caption_file}</figcaption>}
</figure>
);
case 'bookmark':
const href = value.url;
return (
<a href={href} target="_brank">
{href}
</a>
);
default:
return `❌ Unsupported block (${
type === 'unsupported' ? 'unsupported by Notion API' : type
})`;
}
};
export function NotionBlocks({ page, blocks }) {
if (!page || !blocks) return <></>;
let ulList = [];
let olList = [];
let outPutList = '';
const TYPE_UL = 'bulleted_list_item';
const TYPE_OL = 'numbered_list_item';
return (
<>
<Head></Head>
{/* Body */}
<section className={styles.module}>
{/* {blocks.map((block) => renderBlock(block))} */}
{console.log('=====▼開始▼=====')}
{blocks.map((block) => {
console.log(block);
if (block.type === TYPE_UL) {
console.log(block);
ulList.push(block);
console.log(ulList);
console.log('ulです');
} else {
console.log('それ以外です');
}
})}
{console.log('=====▲終了▲=====')}
</section>
</>
);
}
Notionのブロック
追記1
export function NotionBlocks({ page, blocks }) {
if (!page || !blocks) return <></>;
let ulList = [];
let olList = [];
let outPutList = '';
const TYPE_UL = 'bulleted_list_item';
const TYPE_OL = 'numbered_list_item';
console.log(ulList);
return (
<>
<Head></Head>
{/* Body */}
<section className={styles.module}>
{/* {blocks.map((block) => renderBlock(block))} */}
{console.log(ulList)}
{console.log('=====▼開始▼=====')}
{blocks.map((block) => {
console.log(block);
if (block.type === TYPE_UL) {
ulList.push(block);
} else {
}
})}
{console.log('=====▲終了▲=====')}
</section>
</>
);
}
0