メモアプリの作成
カスタムフック化前
- npx create-react-app memo-app –template typescript
- cd memo-app
- mv src/App.tsx src/components/App.tsx
- mkdir src/{components,hooks,types}
- touch src/components/MemoList.tsx
- touch src/types/MemoListProps.ts
import {ChangeEvent, useState, useCallback, FC} from 'react';
import {MemoList} from './MemoList';
const App: FC = () => {
const [text, setText] = useState<string>('')
const [memos, setMemos] = useState<string[]>([])
const onChangeText = (e:ChangeEvent<HTMLInputElement>) => {
setText(e.target.value)
}
const onClickAdd = () => {
setMemos([...memos, text])
setText('')
}
const onClickDelete = useCallback((index: number) => {
const newMemos = [...memos]
newMemos.splice(index, 1)
setMemos(newMemos)
}, [memos])
return (
<>
<input type="text" value={text} onChange={onChangeText}/>
<button onClick={onClickAdd}>追加</button>
<MemoList memos={memos} onClickDelete={onClickDelete}/>
</>
);
}
export default App
import {FC} from 'react';
import type { MemoListProps } from '../types/MemoListProps'
export const MemoList: FC<MemoListProps> = props => {
const {memos, onClickDelete} = props
return (
<ul>
{memos.map((memo, index) => (
<li key={memo}>{memo}<button onClick={() => onClickDelete(index)}>削除</button></li>
))}
</ul>
);
}
export type MemoListProps = {
memos: string[],
onClickDelete: (index: number) => void
}
カスタムフック化
touch src/hooks/useMemoList.ts
import {useCallback, useState} from 'react';
export const useMemoList = () => {
const [memos, setMemos] = useState<string[]>([])
const addMemo = useCallback((text: string) => {
setMemos([...memos, text])
}, [memos])
const deleteMemo = useCallback((index: number) => {
const newMemos = [...memos]
newMemos.splice(index, 1)
setMemos(newMemos)
}, [memos])
return {memos, addMemo, deleteMemo};
}
import {ChangeEvent, useState, useCallback, FC} from 'react';
import {MemoList} from './MemoList';
import {useMemoList} from '../hooks/useMemoList';
const App: FC = () => {
const {memos, addMemo, deleteMemo} = useMemoList()
const [text, setText] = useState<string>('')
const onChangeText = (e:ChangeEvent<HTMLInputElement>) => {
setText(e.target.value)
}
const onClickAdd = () => {
addMemo(text)
setText('')
}
const onClickDelete = useCallback((index: number) => {
deleteMemo(index)
}, [deleteMemo]) // deleteMemoはメモ化されているため依存配列に指定する必要がある
return (
<>
<input type="text" value={text} onChange={onChangeText}/>
<button onClick={onClickAdd}>追加</button>
<MemoList memos={memos} onClickDelete={onClickDelete}/>
</>
);
}
export default App