React useContextとuseStateを使って型定義した配列を制御する

# 概要
[前回の記事](https://qiita.com/RYA234/items/ae1ff4f78dcef3cb74eb "React useContextとuseStateを使ってnumber型の配列を制御する")の応用で、型定義した配列のStateをContext化できるか確認します。

number[]で実装できるか確認する（前回）
↓
CustomType[]で実装できるか確認する。（本記事）
実装した感想としては、number[]で実装できれば型定義した配列も簡単です。


# 開発環境
React:18.20
css:sytled-jsx
言語:TypeScript
# 実装
以下３つのファイルを作成します。
ファイル名|説明
---|--
subItems.d.ts|型定義ファイル
index.tsx|親コンポーネント
subItemComponent.tsx|子コンポーネント


# 図
完成図の様子
下gifアニメはボタンを押して、コンポーネントの増減、コンポーネントの要素（amount）を増やす操作を行っている様子です。
![Context_例.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710623/bd76cd7e-5887-636a-89b8-cd4b6cd9d65b.gif)




ボタン類の説明
ボタンの名前|機能
---|----
削除|対応する子コンポーネントが削除されます。
追加|対応する子コンポーネントのamountが+1されます。
子コンポーネントを追加|子コンポーネントを追加します。


# 実装
## 1.型定義ファイルの作成
```ts:subItem.d.ts
export type SubItem={
	id: number;
	name:string;
	price:number;
	amount:number;
}

```

## 2.親コンポーネントの作成(Providerの設定)
詰まった点は特になし
```tsx:index.tsx
import { createContext, useState } from "react"
import SubItemComponent from "./subItemComponent";
import { SubItem } from "./subItem"

export const SubItemContext = createContext();

export default function Index() {
	const[subItems, setSubItems] = useState<SubItem[]>([{id: 1, name: "subItem1",price:100,amount:1}, {id: 2, name: "subItem2",price:300,amount:1}])

    // 各要素のnameを出力
	let message : string = ""
	subItems.map((item: SubItem, index: number) => {
		message += item.name + " ,"
	})
	console.log(message)
	return(
		<>

			{subItems.map((item: SubItem, index: number) => {
				const values={subItems,setSubItems,index}
				return(
					<SubItemContext.Provider value={values} key={index}>
						<SubItemComponent />
					</SubItemContext.Provider>
				)
			})}
			<div></div>
		</>
	)
}

```

## 3.子コンポーネントの作成（useContextの設定）
詰まった点は特になし
```tsx:subItemComponent.tsx
import { useContext } from "react"
import { SubItemContext } from "."
import { SubItem } from "./subItem"

export default function SubItemComponent() {
	const {subItems,setSubItems,index} = useContext(SubItemContext)	
	return(
		<div className="Layout">
			<div className="SubLayout">
				<div>ID：{subItems[index].id}</div>
				<div>名前：{subItems[index].name}</div>
			</div>
			<div className="SubLayout">
				<div>値段：{subItems[index].price}</div>
				<div>個数：{subItems[index].amount}</div>
			</div>
			<style jsx>{`				
				// 枠線-要素を縦方向に配置
				.Layout{
					display:flex;
					flex-direction:column;
					border:1px solid black;
					width:200px;
				}
				//要素を横方向に配置
				.SubLayout{
					display:flex;
					flex-direction:row;
				}
			`}</style>	

		</div>
	)

}

```
## 4.ボタン系の実装

```diff_tsx:index.tsx

import { createContext, useState } from "react"
import SubItemComponent from "./subItemComponent";
import { SubItem } from "./subItem"

export const SubItemContext = createContext();

export default function Index() {
	const[subItems, setSubItems] = useState<SubItem[]>([{id: 1, name: "subItem1",price:100,amount:1}, {id: 2, name: "subItem2",price:300,amount:1}])

+	const addSubItemHandler = () => {
+		setSubItems([...subItems,{id: subItems.length + 1, name: "subItem" + (subItems.length + 1),price:100,amount:1}])
+	}
	let message : string = ""
	subItems.map((item: SubItem, index: number) => {
		message += item.name + " ,"
	})
	console.log(message)
	return(
		<>

			{subItems.map((item: SubItem, index: number) => {
				const values={subItems,setSubItems,index}
				return(
					<SubItemContext.Provider value={values} key={index}>
						<SubItemComponent />
					</SubItemContext.Provider>
				)
			})}
			<div></div>
+			<button onClick={addSubItemHandler}>子コンポーネントを追加</button>
		</>
	)
}
```


```diff_tsx:subItemComponent.tsx
import { useContext } from "react"
import { SubItemContext } from "."
import { SubItem } from "./subItem"

export default function SubItemComponent() {
	const {subItems,setSubItems,index} = useContext(SubItemContext)	

+	// amountを+1する
+	const addHandler = () => {
+		setSubItems(subItems.map((subItem:SubItem,i:number) => i === index ?
+		// amountだけ更新したら、他の要素が消えるので全部（id,name,price）更新する必要あり
+		 {id:subItem.id,amount:subItem.amount + 1,name:subItem.name,price:subItem.price} : subItem)
+	)}


	// 子コンポーネントの存在を削除する
+	const deleteHandler = ()=>{
+		setSubItems(subItems.filter((subItem:SubItem,i:number) => i !== index))
+	}
	return(
		<div className="Layout">
			<div className="SubLayout">
				<div>ID：{subItems[index].id}</div>
				<div>名前：{subItems[index].name}</div>
			</div>
			<div className="SubLayout">
				<div>値段：{subItems[index].price}</div>
				<div>個数：{subItems[index].amount}</div>
			</div>
			<div className="SubLayout">
+				<button onClick={addHandler}>追加</button>
+				<button onClick={deleteHandler}>削除</button>
			</div>
			<style jsx>{`				
				// 枠線-要素を縦方向に配置
				.Layout{
					display:flex;
					flex-direction:column;
					border:1px solid black;
					width:200px;
				}
				//要素を横方向に配置
				.SubLayout{
					display:flex;
					flex-direction:row;
				}
			`}</style>	

		</div>
	)

}

```