概要
前回の記事の応用で、型定義した配列のStateをContext化できるか確認します。
number[]で実装できるか確認する(前回)
↓
CustomType[]で実装できるか確認する。(本記事)
実装した感想としては、number[]で実装できれば型定義した配列も簡単です。
開発環境
React:18.20
css:sytled-jsx
言語:TypeScript
実装
以下3つのファイルを作成します。
ファイル名 | 説明 |
---|---|
subItems.d.ts | 型定義ファイル |
index.tsx | 親コンポーネント |
subItemComponent.tsx | 子コンポーネント |
図
完成図の様子
下gifアニメはボタンを押して、コンポーネントの増減、コンポーネントの要素(amount)を増やす操作を行っている様子です。
ボタン類の説明
ボタンの名前 | 機能 |
---|---|
削除 | 対応する子コンポーネントが削除されます。 |
追加 | 対応する子コンポーネントのamountが+1されます。 |
子コンポーネントを追加 | 子コンポーネントを追加します。 |
実装
1.型定義ファイルの作成
subItem.d.ts
export type SubItem={
id: number;
name:string;
price:number;
amount:number;
}
2.親コンポーネントの作成(Providerの設定)
詰まった点は特になし
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の設定)
詰まった点は特になし
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.ボタン系の実装
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>
</>
)
}
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>
)
}