最後の4日間は、アプリケーションらしい UI のサンプルを紹介していきます。本日はメッセージアプリでよく見る chat board です。
なお、残念ながら本作は現状 iOS safari などで下部固定入力エリアの挙動不審が解決していません。メッセージが追加される毎にスクロール位置を移動し吹き出しを表示する処理はどこかで利用出来ると思います。
code: github / $ yarn 1222
メッセージの入力
Input
コンポーネントは、内部で入力テキストを保持しています。onCommitMessage
の callback で、items
を更新します。
components/index.tsx
export default (props: Props) => {
const [items, updateItems] = useState<Item[]>([])
return (
<div className={props.className}>
<List items={items} />
<Input
onCommitMessage={message => {
const newItem: Item = {
createdAt: new Date(),
message
}
updateItems(_items => [...items, newItem])
}}
/>
</div>
)
}
メッセージの挿入
props.items
で受領するメッセージ配列が変化した時、吹き出しコンポーネントを生成、コンポーネント用配列に保持します。こうすることで、マウント済みコンポーネントの rerender 抑止をすることが出来ます。
components/list.tsx
const View = (props: Props) => {
const nodeRef = useRef({} as HTMLDivElement)
const [items, updateItems] = useState<JSX.Element[]>([])
useEffect(() => {
if (nodeRef.current === null) return
const {
height
} = nodeRef.current.getBoundingClientRect()
window.scrollTo(0, height)
})
useEffect(
() => {
if (props.items.length === 0) return
updateItems(_items => {
const newItem = props.items[props.items.length - 1]
_items.push(
<ItemComponent
key={newItem.createdAt.getTime()}
createdAtLabel={getTimeLabel(newItem.createdAt)}
message={newItem.message}
/>
)
return _items
})
},
[props.items]
)
return (
<div className={props.className} ref={nodeRef}>
{items}
</div>
)
}