Skip to content

Commit

Permalink
resultの結果をuseEffectからuseContextに移行
Browse files Browse the repository at this point in the history
  • Loading branch information
MurakawaTakuya committed Jan 14, 2025
1 parent 8e9227c commit dfb56bb
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 64 deletions.
43 changes: 43 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
module.exports = [
{
env: {
browser: true,
es2021: true,
},
extends: [
"plugin:react/recommended",
"prettier",
"next",
"next/core-web-vitals",
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/stylistic",
"plugin:react-hooks/recommended",
"plugin:@next/next/recommended",
],
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
plugins: ["react", "eslint-plugin-unused-imports"],
rules: {
"unused-imports/no-unused-imports": "error",
},
ignorePatterns: [
"node_modules",
".next",
"out",
"package.json",
"package-lock.json",
],
},
{
files: [".eslintrc.{js,cjs}"],
env: {
node: true,
},
parserOptions: {
sourceType: "script",
},
},
];
3 changes: 2 additions & 1 deletion functions/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
"skipLibCheck": true
},
"compileOnSave": true,
"include": ["src/*", "functions/src/**/*", "src/**/*.ts"]
"include": ["src/*", "functions/src/**/*", "src/**/*.ts"],
"exclude": ["node_modules"]
}
109 changes: 60 additions & 49 deletions src/Components/DashBoard/DashBoard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
fetchResult,
handleFetchResultError,
} from "@/utils/API/Result/fetchResult";
import { useResults } from "@/utils/ResultContext";
import { useUser } from "@/utils/UserContext";
import CircularProgress from "@mui/joy/CircularProgress";
import Typography from "@mui/joy/Typography";
Expand All @@ -30,23 +31,22 @@ export default function DashBoard({
pending?: boolean;
orderBy?: "asc" | "desc";
}) {
const [successResults, setSuccessResults] = useState<GoalWithIdAndUserData[]>(
[]
);
const [failedResults, setFailedResults] = useState<GoalWithIdAndUserData[]>(
[]
);
const [pendingResults, setPendingResults] = useState<GoalWithIdAndUserData[]>(
[]
);
const {
successResults,
setSuccessResults,
failedResults,
setFailedResults,
pendingResults,
setPendingResults,
} = useResults();
const [noResult, setNoResult] = useState<boolean>(false);
const [isLoading, setIsLoading] = useState<boolean>(true);
const [reachedBottom, setReachedBottom] = useState<boolean>(false);
const [isLoadingMore, setIsLoadingMore] = useState<boolean>(false);
const bottomRef = useRef<HTMLDivElement>(null);
const isAlreadyFetching = useRef(false);
const offset = useRef(0);
const noMore = useRef(false);
const [noMorePending, setNoMorePending] = useState<boolean>(false);
const [noMoreFinished, setNoMoreFinished] = useState<boolean>(false);

const [lastPostDate, setLastPostDate] = useState<string | null>(null); // 投稿が0の場合はnull

Expand All @@ -55,38 +55,9 @@ export default function DashBoard({

const limit = 10; // limitずつ表示

useEffect(() => {
setTimeout(() => {
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting && !isLoading && !noMore.current) {
setReachedBottom(true);
fetchData();
}
},
{ threshold: 1 }
);

if (bottomRef.current) {
observer.observe(bottomRef.current);
}

return () => {
if (bottomRef.current) {
observer.disconnect();
}
};
}, 1000);
}, [isLoading, noMore.current, bottomRef.current, bottomRef]);

useEffect(() => {
offset.current =
successResults.length + failedResults.length + pendingResults.length;
}, [successResults, failedResults, pendingResults]);

const fetchData = () => {
if (noMore.current) {
return;
if ((pending && noMorePending) || (success && failed && noMoreFinished)) {
return; // TODO: うまく動作していない
}
if (isAlreadyFetching.current) {
return;
Expand All @@ -96,12 +67,15 @@ export default function DashBoard({
if (reachedBottom && !isLoadingMore) {
setIsLoadingMore(true);
}
const offset = pending
? pendingResults.length
: successResults.length + failedResults.length;
fetchResult({
userId,
success,
failed,
pending,
offset: offset.current,
offset,
limit,
})
.then((data) => {
Expand All @@ -128,13 +102,16 @@ export default function DashBoard({
return [...prev, ...newResults];
});

if (pending && data.pendingResults.length < limit) {
setNoMorePending(true);
}

if (
data.successResults.length +
data.failedResults.length +
data.pendingResults.length <
limit
success &&
failed &&
data.successResults.length + data.failedResults.length < limit
) {
noMore.current = true;
setNoMoreFinished(true);
}

setIsLoading(false);
Expand All @@ -153,6 +130,36 @@ export default function DashBoard({
});
};

// 画面下に到達したことを検知
useEffect(() => {
setTimeout(() => {
const observer = new IntersectionObserver(
(entries) => {
if (
entries[0].isIntersecting &&
!isLoading &&
((pending && !noMorePending) ||
(success && failed && !noMoreFinished))
) {
setReachedBottom(true);
fetchData();
}
},
{ threshold: 1 }
);

if (bottomRef.current) {
observer.observe(bottomRef.current);
}

return () => {
if (bottomRef.current) {
observer.disconnect();
}
};
}, 1000);
}, [isLoading, noMorePending, noMoreFinished, bottomRef.current, bottomRef]);

useEffect(() => {
rerenderDashBoard = fetchData;
if (userId) {
Expand Down Expand Up @@ -197,6 +204,7 @@ export default function DashBoard({
return (
<>
{isLoading ? (
// ロード中
<LinearProgress
sx={{
width: "90%",
Expand All @@ -205,6 +213,7 @@ export default function DashBoard({
}}
/>
) : noResult ? (
// 目標や投稿が無い場合
<CenterIn delay={1}>
<Typography
level="h4"
Expand All @@ -224,7 +233,9 @@ export default function DashBoard({
/>
<div className="bottom" ref={bottomRef}></div>

{!noMore.current &&
{/* 下に到達した時に続きを表示 */}
{((pending && !noMorePending) ||
(success && failed && !noMoreFinished)) &&
(reachedBottom ? (
<div
style={{
Expand Down
2 changes: 1 addition & 1 deletion src/Components/DeleteGoalModal/DeleteGoalModal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";
import { appCheckToken, functionsEndpoint } from "@/app/firebase";
import { triggerDashBoardRerender } from "@/Components/DashBoard/DashBoard"; // インポートパスを修正
import { triggerDashBoardRerender } from "@/Components/DashBoard/DashBoard";
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
import { DialogContent, DialogTitle, Modal, ModalDialog } from "@mui/joy";
import JoyButton from "@mui/joy/Button";
Expand Down
8 changes: 4 additions & 4 deletions src/Components/GoalModal/CreateGoalModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default function CreateGoalModal({
const localNextDay = new Date(
nextDay.getTime() - nextDay.getTimezoneOffset() * 60000
);
setDeadline(localNextDay.toISOString().slice(0, 16));
return localNextDay.toISOString().slice(0, 16);
};

useEffect(() => {
Expand All @@ -56,7 +56,7 @@ export default function CreateGoalModal({
localDate.setDate(localDate.getDate() + 1); // 明日にする
setDeadline(localDate.toISOString().slice(0, 16));
} else {
resetDeadline();
setDeadline(resetDeadline());
}
}, [defaultText, defaultDeadline]);

Expand All @@ -78,8 +78,8 @@ export default function CreateGoalModal({
});
triggerDashBoardRerender();

setText("");
resetDeadline();
setText(defaultText || "");
setDeadline(defaultDeadline || resetDeadline());
setOpen(false);
} catch (error: unknown) {
console.error("Error creating goal:", error);
Expand Down
5 changes: 4 additions & 1 deletion src/app/discover/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import DashBoard, {
triggerDashBoardRerender,
} from "@/Components/DashBoard/DashBoard";
import GoalModalButton from "@/Components/GoalModal/GoalModalButton";
import { ResultProvider } from "@/utils/ResultContext";
import { useEffect } from "react";

export default function Discover() {
Expand All @@ -12,7 +13,9 @@ export default function Discover() {

return (
<>
<DashBoard pending={false} />
<ResultProvider>
<DashBoard pending={false} />
</ResultProvider>
<GoalModalButton />
</>
);
Expand Down
23 changes: 15 additions & 8 deletions src/app/mycontent/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client";
import DashBoard from "@/Components/DashBoard/DashBoard";
import GoalModalButton from "@/Components/GoalModal/GoalModalButton";
import { ResultProvider } from "@/utils/ResultContext";
import { useUser } from "@/utils/UserContext";
import Typography from "@mui/joy/Typography";
import { styled } from "@mui/material/styles";
Expand Down Expand Up @@ -62,15 +63,21 @@ export default function MyContent() {
メールに届いた認証リンクを確認してください。
</Typography>
) : value === "pending" ? (
<DashBoard
key="pending"
userId={user?.userId}
success={false}
failed={false}
orderBy="asc"
/>
<ResultProvider>
<DashBoard
key="pending"
userId={user?.userId}
success={false}
failed={false}
orderBy="asc"
/>
</ResultProvider>
) : (
<DashBoard key="finished" userId={user?.userId} pending={false} />
value === "finished" && (
<ResultProvider>
<DashBoard key="finished" userId={user?.userId} pending={false} />
</ResultProvider>
)
)}

<GoalModalButton />
Expand Down
54 changes: 54 additions & 0 deletions src/utils/ResultContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { GoalWithIdAndUserData } from "@/types/types";
import React, { createContext, ReactNode, useContext, useState } from "react";

type ResultContextType = {
successResults: GoalWithIdAndUserData[];
setSuccessResults: React.Dispatch<
React.SetStateAction<GoalWithIdAndUserData[]>
>;
failedResults: GoalWithIdAndUserData[];
setFailedResults: React.Dispatch<
React.SetStateAction<GoalWithIdAndUserData[]>
>;
pendingResults: GoalWithIdAndUserData[];
setPendingResults: React.Dispatch<
React.SetStateAction<GoalWithIdAndUserData[]>
>;
};

const ResultContext = createContext<ResultContextType | undefined>(undefined);

export const ResultProvider = ({ children }: { children: ReactNode }) => {
const [successResults, setSuccessResults] = useState<GoalWithIdAndUserData[]>(
[]
);
const [failedResults, setFailedResults] = useState<GoalWithIdAndUserData[]>(
[]
);
const [pendingResults, setPendingResults] = useState<GoalWithIdAndUserData[]>(
[]
);

return (
<ResultContext.Provider
value={{
successResults,
setSuccessResults,
failedResults,
setFailedResults,
pendingResults,
setPendingResults,
}}
>
{children}
</ResultContext.Provider>
);
};

export const useResults = () => {
const context = useContext(ResultContext);
if (!context) {
throw new Error("useResults must be used within a ResultProvider");
}
return context;
};

0 comments on commit dfb56bb

Please sign in to comment.