タスク一覧を「判断できる画面」へ。React初心者がDashboardに込めた3つの工夫
こんばんは、しゅんです。
仕事や育児の合間にReactやTypeScriptを学びながら、勤怠・タスク管理アプリ「Team Visualizer」を制作しています。
前回は、職場で感じた仕事量の偏りと、Team Visualizerを作り始めた理由について書きました。
今回からは、実際に制作した画面について紹介していきます。
最初に紹介するのは、アプリの中心となるDashboardです。
Dashboardは、ただ数字を並べる画面ではない
Team Visualizerには、タスクを登録・管理するTasksページがあります。
Tasksページは次回お見せします。
Tasksページでは、次のような情報を登録しています。
タスク名
担当者
期限
ステータス
優先度
進捗率
しかし、タスクが一覧で並んでいるだけでは、チーム全体の状況をすぐに把握することはできません。
例えば、
「現在、対応中の仕事はいくつあるのか」
「誰に仕事が集中しているのか」
「期限が近い仕事は何か」
を確認するには、一つひとつのタスクを見る必要があります。
そこで、Tasksページに登録されたデータを集計し、チームが次に何をするべきか判断できる画面としてDashboardを作りました。
Dashboardで特に意識したのは、次の3つです。
チーム全体の状況をすぐに確認できる
仕事の偏りと期限を確認できる
最近追加されたタスクを確認できる

1.チーム全体の状況をカードで確認する
Dashboardで最も重要視したのが、画面上部にある4つのカードです。
進行中タスク
完了タスク
期限超過タスク
全体の進捗率
この4つを画面上部に配置した理由は、Dashboardを開いたときに、チーム全体の状況を短時間で確認できるようにするためです。

この数字をDashboard専用に入力しているわけではありません。
Tasksページで登録したタスクを読み込み、ステータスや期限、進捗率をもとに計算しています。
流れを簡単に表すと、次のようになります。
Tasksページでタスクを登録
↓
localStorageに保存
↓
Dashboardでタスクを読み込む
↓
ステータスなどをもとに集計する
useStateでタスクを管理する
Dashboardでは、読み込んだタスクを管理するためにuseStateを使用しています。
const [tasks, setTasks] = useState<Task[]>([]);それぞれの役割は次のとおりです。
tasks:Dashboardで使用するタスクの一覧
setTasks:タスクの一覧を更新する関数
[]:最初はタスクが入っていない空の配列
useStateは、画面の中で変化する値をReactに覚えてもらうための機能です。
今回の場合は、Tasksページから読み込んだタスクをtasksとして覚えてもらいます。
setTasksによってタスクが更新されると、ReactがDashboardを自動的に再表示してくれます。
私の中では、useStateを「画面が覚えておくメモ」と考えると理解しやすくなりました。
useEffectでlocalStorageから読み込む
Tasksページで登録したタスクは、localStorageに保存しています。
Dashboardを表示したときにlocalStorageからタスクを読み込むため、useEffectを使用しています。
useEffect(() => {
const savedTasks = localStorage.getItem("tasks");
if (savedTasks) {
setTasks(JSON.parse(savedTasks));
}
}, []);処理の流れは次のとおりです。
Dashboardを表示する
localStorageからタスクを取得する
保存されている文字列を配列に戻す
setTasksでタスクを更新する
Dashboardが再表示される
最後にある[]は、この処理をDashboardが最初に表示されたときに実行するための指定です。
}, []);今回のコードでは、次のように役割を分けています。
useState:読み込んだタスクを覚える
useEffect:localStorageからタスクを読み込む
filterを使ってタスク数を計算する
進行中・完了・期限超過の件数は、tasksから条件に合うデータを絞り込んで計算します。
そこで使用するのがfilter()です。
進行中のタスク
const inProgressCount = tasks.filter(
(task) => task.status === "進行中"
).length;filter()でステータスが「進行中」のタスクだけを残し、最後のlengthで件数を取得しています。
完了したタスク
const completedCount = tasks.filter(
(task) => task.status === "完了"
).length;こちらは、ステータスが「完了」のタスクだけを数えています。
期限を過ぎたタスク
const overdueCount = tasks.filter((task) => {
const isNotCompleted = task.status !== "完了";
const isOverdue = new Date(task.deadline) < new Date();
return isNotCompleted && isOverdue;
}).length;期限超過では、次の2つを確認しています。
タスクが完了していない
期限が今日より前になっている
この両方に当てはまるタスクを、期限超過として数えています。
全体の進捗率を計算する
全体の進捗率は、それぞれのタスクが持っているprogressを合計し、タスク数で割って求めます。
const totalProgress = tasks.reduce(
(total, task) => total + task.progress,
0
);
const overallProgress =
tasks.length === 0
? 0
: Math.round(totalProgress / tasks.length);ここではreduce()を使っています。
reduce()は、配列に入っている複数の値を一つにまとめるときに使用します。
今回の場合は、すべてのタスクの進捗率を合計しています。
タスクが0件のときに割り算をすると正しく計算できないため、tasks.length === 0の場合は進捗率を0にしています。
Dashboardに表示している4つの数字は、別々に入力しているわけではありません。
Tasksページのデータから、自動的に計算して表示しています。
この仕組みによって、Tasksページのタスクが変われば、Dashboardの数字にも反映できるようになります。
2.仕事の偏りと近日中の期限を確認する
Dashboardの中央には、次の2つを並べています。
メンバーの負荷タスク
近日中の期限

メンバーの負荷タスク
メンバーの負荷タスクでは、担当者ごとのタスク数を表示しています。
数字だけでは仕事量の違いを比較しにくいため、タスク数に合わせてバーの長さが変わるようにしました。
担当者ごとのタスクは、filter()を使って絞り込みます。
const memberTasks = tasks.filter(
(task) => task.assignee === member.name
);例えば、担当者が「Aさん」の場合は、assigneeが「Aさん」になっているタスクだけを取り出します。
その件数をもとにバーの長さを変えることで、誰に仕事が集中しているのかを視覚的に確認できます。
この機能は、Team Visualizerを作ろうと思った理由である「仕事量の偏りを見える化すること」に直接つながっています。
近日中の期限
近日中の期限では、完了していないタスクを期限が近い順に並べています。
const upcomingTasks = [...tasks]
.filter((task) => task.status !== "完了")
.sort(
(a, b) =>
new Date(a.deadline).getTime() -
new Date(b.deadline).getTime()
)
.slice(0, 4);ここでは、3つの処理をつなげています。
filter()で完了していないタスクを残す
sort()で期限が近い順に並べる
slice(0, 4)で最初の4件を取り出す
期限を表示することで、優先して対応する必要がある仕事に気づきやすくしています。
3.追加されたタスクの状況を確認する
Dashboardの下部には、Tasksページで追加したタスクを表示しています。

表示する主な情報は次のとおりです。
タスク
プロジェクト
担当者
ステータス
進捗率
現在は、tasks配列に保存されている順番を使い、先頭から4件を表示しています。
const displayedTasks = tasks.slice(0, 4);slice(0, 4)は、タスク一覧の0番目から4件を取り出す処理です。
画面では、map()を使ってタスクを一つずつ表示します。
{displayedTasks.map((task) => (
<tr key={task.id}>
<td>{task.title}</td>
<td>{task.project}</td>
<td>{task.assignee}</td>
<td>{task.status}</td>
<td>{task.progress}%</td>
</tr>
))}map()は、配列に入っているデータを一つずつ取り出し、同じ形で画面に表示するときに使用します。
Tasksページで追加されたデータをDashboardでも使用することで、同じタスクをもう一度入力する必要がありません。
難しそうに見えても、処理を分ければ理解できる
Dashboard全体を見ると、多くの数字やカード、グラフが表示されているため、最初は難しい画面に見えます。
しかし、実際に行っていることを分けると、次のようになります。
useEffectでタスクを読み込む
useStateでタスクを管理する
filter()で必要なタスクを絞り込む
reduce()で進捗率を計算する
sort()で期限順に並べる
slice()で表示件数を絞る
map()で画面に表示する
私も最初から、すべての処理を理解して作れたわけではありません。
一つの大きなDashboardを作ろうとするのではなく、最初はカードを一つ作り、次にタスク数を表示し、そのあとに期限やメンバーの負荷を追加しました。
小さな処理を一つずつ組み合わせたことで、少しずつ現在のDashboardになりました。
これからオリジナルアプリを作る方へ
オリジナルアプリを作るときは、最初から難しい機能を考える必要はないと思います。
まずは、
「自分が困っていることは何か」
「画面を見た人に何を伝えたいか」
を考えるところから始められます。
私の場合は、「チームの状況を短時間で確認したい」という目的から、Dashboardの4つのカードを作りました。
目的が決まると、必要なデータや機能も少しずつ見えてきます。
この記事を読んで、
「複雑そうな画面も、小さな処理の組み合わせで作れる」
「自分にもオリジナルアプリを作れそう」
と思ってもらえたらうれしいです。
最後に
今回は、Team VisualizerのDashboardに込めた3つの工夫を紹介しました。
特に重要視したのは、Tasksページのデータをもとに、チーム全体の状況を4つのカードで確認できるようにしたことです。
Dashboardは、ただ情報を並べる画面ではありません。
登録されたタスクを、チームが次に何をすべきか判断できる情報へ変える画面です。
Team Visualizerには、まだ未完成の部分も多くあります。今後も、制作を通して学んだことやエラーの解決方法を記録しながら、少しずつ改善を重ねて完成に近づけていきます。
最後まで読んでいただき、ありがとうございました。
