はじめに
みなさんこんにちは!
株式会社hokanでフロントエンドエンジニアをしている斉藤と申します!!
初投稿でございます。どうぞよろしくお願いします
今回はReactでJiraのスクラムボード機能の簡易版を作ったので紹介していこうと思います。
早速ですが成果物はこちらです!
https://jira-clone-4e8c1.web.app
使った技術
- react-hook-form
- react-beautiful-dnd
- Firebase
- chakra-ui
react-hook-form
react-hook-formはreactのhooksのような感じで使えるフォームライブラリで、react-hook-form側がなんでもやってくれるので、パフォーマンスもよくコードの記述量も減り、onChangeなどを発火させる必要もなくstate管理も楽なパーフェクトライブラリです。
今回はこんな感じで使いました。
import {useForm} from 'react-hook-form'
const {
register,
formState: { errors, isSubmitting },
handleSubmit,
} = useForm({
defaultValues: { ...initialValues },
});
<form onSubmit={onSubmit("create")}>
<ModalBody>
<FormControl id="stageItem" isInvalid={!!errors.title}>
<FormLabel>タイトル</FormLabel>
<Input
id="title"
{...register("title", {
required: "タイトルは必須です",
})}
autoFocus
/>
<FormErrorMessage>
{errors.title && errors.title.message}
</FormErrorMessage>
<Spacer h={4} />
<FormLabel>内容</FormLabel>
<Textarea id="content" {...register("content")} />
<Spacer h={4} />
<FormLabel>ステータス</FormLabel>
<Select {...register("statusId")}>
{Object.values(DefaultTaskStatusMap).map((status) => (
<option key={status.id} value={status.id}>
{status.title}
</option>
))}
</Select>
</FormControl>
</ModalBody>
</form>
register
でコンポーネントをフックに登録したら、フォームフィールドの値のバリデーションと値を検知することができるようになり、handleSubmit
の引数にhandleSubmit(customOnSubmit)
みたいな感じで渡してあげるとcustomOnSubmit
の引数にフォームの入力値が入ってきます。
エラーを検知した場合はformState
にエラーメッセージが入ってくるのでエラーハンドリングもピャピャッとできちゃいます。
簡単にフォームをコントロールできちゃいますね。
react-beautiful-dnd
react-beatutiful-dndはドラッグ&ドロップが簡単にできるライブラリです。
<DragDropContext />
でドラッグ&ドロップしたいエリアをラップしてあげて、
<Droppable />
でラップしたエリアにドロップできるようになり、
<Draggable />
でラップしたアイテムがドラッグできるようになります。
こちらのコードのようになるイメージですね!
export const Content: React.FC<Props> = () => {
const [items, setItems] = useState(_items);
const onDragEnd = (result: DropResult) => {
if (!result.destination) return;
const startIndex = result.source.index;
const endIndex = result.destination.index;
const newItems = arrayMove(items, startIndex, endIndex);
setItems(newItems);
};
return (
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId={"droppable"} direction={"vertical"}>
{(provided) => (
<div ref={provided.innerRef} {...provided.droppableProps}>
{items.map((item, index) => (
<Draggable
key={index}
index={index}
draggableId={String(item.id)}
>
{(provided) => (
<p
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
{item.title}
</p>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
);
};
Firebase
firebaseは今回、firestoreとhostingとauthを利用しました。
詳細な説明は省きますが個人で軽くアプリを作りたい時などにおすすめです!
authは今回はGoogle認証だけ使用しました!
こちらはとても簡単でfirebaseのコンソールでGoogle認証をONにしていただいた後、このコードで簡単にGoogle認証できちゃいます!
const signInWithGoogle = () => {
const provider = new firebase.auth.GoogleAuthProvider();
firebase.auth().signInWithPopup(provider);
};
firestoreはフロントだけでデータのやりとりができサクッとアプリを作りたいときにいいですね!
hostingは
- firebase cli をインストール
- firebase init hosting
- firebase deploy --only hosting
の3ステップで完了するのでとても簡単ですね。
chakra-ui
今回一番感動したのはなんといってもこちら、chakra-uiです。
chakra-uiはReact用のコンポーネントライブラリで少ないコードで「いい感じのui」ができちゃう優れものです。
こんな感じでコンポーネントを<ChakraProvider><ChakraProvider/>
でラップしてあげるとchakra-uiのテーマが使えるようになり、ダークテーマにも対応しています。
import Routes from "Routes";
import { ChakraProvider } from "@chakra-ui/react";
function App() {
return (
<ChakraProvider>
<Routes />
</ChakraProvider>
);
}
export default App;
どれくらい簡単「いい感じのUI」ができるのかサンプルを用意しました!
import {
Modal as ChakraModal,
ModalBody,
ModalOverlay,
ModalContent,
ModalHeader,
ModalCloseButton,
ModalFooter,
Button,
HStack,
FormControl,
FormLabel,
Input,
Spacer,
Textarea,
Select,
useDisclosure,
} from "@chakra-ui/react";
export const Modal: React.FC = () => {
const { isOpen, onClose } = useDisclosure();
return (
<ChakraModal onClose={onClose} isOpen={isOpen} isCentered>
<ModalOverlay />
<ModalContent>
<ModalHeader>{"タスクを作成"}</ModalHeader>
<ModalCloseButton />
<ModalBody>
<FormControl id="stageItem">
<FormLabel>タイトル</FormLabel>
<Input />
<Spacer h={4} />
<FormLabel>内容</FormLabel>
<Textarea />
<Spacer h={4} />
<FormLabel>ステータス</FormLabel>
<Select>
{Object.values(DefaultTaskStatusMap).map((status) => (
<option key={status.id} value={status.id}>
{status.title}
</option>
))}
</Select>
</FormControl>
</ModalBody>
<ModalFooter>
<HStack spacing={3}>
<Button>{"タスクを作成"}</Button>
<Button onClick={onClose}>キャンセル</Button>
</HStack>
</ModalFooter>
</ModalContent>
</ChakraModal>
);
};
このモーダル、chakra-uiのコンポーネントだけで作っているのですがほとんどカスタムしてません。
const { isOpen, onClose, onOpen } = useDisclosure()
でモーダルやダイアログ、ドロワーなどの状態を管理できたりします。
自分で作るとちょっと面倒なUIも作ってくれており、デフォルトでこんないい感じのUI作れちゃうのはとても気持ちがいいですね。
他にもaria属性の定義や、キーボードナビゲーションへの配慮もあるのでアクセシビリティも問題ありません。
chakra-uiは今後も積極的に使っていきたいと強く思いました!
所感
今回はやってみた系の記事でベテランエンジニアの方には特に学ぶこともなかったかと思いますが、駆け出しエンジニアの皆様やフロントやったことないエンジニアの方には役に立っていただけたら嬉しいです!
初めての記事でとても大変でしたがこれからも頑張って描いていこうと思いました。