fix broken memoization of question card component

This commit is contained in:
nflnkr 2024-02-25 14:12:41 +03:00
parent 4e915f3e39
commit 69e76397f5
11 changed files with 78 additions and 136 deletions

@ -1,6 +1,3 @@
import { DoubleArrowRight } from "@icons/questionsPage/DoubleArrowRight";
import { DoubleTick } from "@icons/questionsPage/DoubleTick";
import { VectorQuestions } from "@icons/questionsPage/VectorQuestions";
import { DeleteIcon } from "@icons/questionsPage/deleteIcon";
import type { SxProps } from "@mui/material";
import {
@ -8,7 +5,6 @@ import {
Button,
IconButton,
Modal,
Tooltip,
Typography,
useMediaQuery,
useTheme,
@ -17,27 +13,17 @@ import {
copyQuestion,
deleteQuestion,
deleteQuestionWithTimeout,
clearRuleForAll,
updateQuestion,
getQuestionByContentId,
} from "@root/questions/actions";
import { useQuestionsStore } from "@root/questions/store";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { updateDesireToOpenABranchingModal } from "@root/uiTools/actions";
import MiniButtonSetting from "@ui_kit/MiniButtonSetting";
import { DeleteFunction } from "@utils/deleteFunc";
import { useState } from "react";
import { CopyIcon } from "../../assets/icons/questionsPage/CopyIcon";
import Branching from "../../assets/icons/questionsPage/branching";
import Clue from "../../assets/icons/questionsPage/clue";
import { HideIcon } from "../../assets/icons/questionsPage/hideIcon";
import SettingIcon from "../../assets/icons/questionsPage/settingIcon";
import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { enqueueSnackbar } from "notistack";
import { useQuestionsStore } from "@root/questions/store";
import { updateOpenedModalSettingsId } from "@root/uiTools/actions";
import { updateRootContentId } from "@root/quizes/actions";
import { useUiTools } from "@root/uiTools/store";
import { useState } from "react";
import { updateSomeWorkBackend } from "@root/uiTools/actions";
import { DeleteFunction } from "@utils/deleteFunc";
interface Props {
switchState: string;
@ -306,7 +292,7 @@ export default function ButtonsOptions({
setOpenDelete(true);
} else {
deleteQuestionWithTimeout(question.id, () =>
DeleteFunction(questions, question, quiz),
DeleteFunction(question),
);
}
}}
@ -351,7 +337,7 @@ export default function ButtonsOptions({
sx={{ minWidth: "150px" }}
onClick={() => {
deleteQuestionWithTimeout(question.id, () =>
DeleteFunction(questions, question, quiz),
DeleteFunction(question),
);
}}
>

@ -1,6 +1,3 @@
import { DoubleArrowRight } from "@icons/questionsPage/DoubleArrowRight";
import { DoubleTick } from "@icons/questionsPage/DoubleTick";
import { VectorQuestions } from "@icons/questionsPage/VectorQuestions";
import { DeleteIcon } from "@icons/questionsPage/deleteIcon";
import type { SxProps } from "@mui/material";
import {
@ -8,7 +5,6 @@ import {
Button,
IconButton,
Modal,
Tooltip,
Typography,
useMediaQuery,
useTheme,
@ -17,27 +13,17 @@ import {
copyQuestion,
deleteQuestion,
deleteQuestionWithTimeout,
clearRuleForAll,
updateQuestion,
getQuestionByContentId,
} from "@root/questions/actions";
import { useQuestionsStore } from "@root/questions/store";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { updateDesireToOpenABranchingModal } from "@root/uiTools/actions";
import MiniButtonSetting from "@ui_kit/MiniButtonSetting";
import { CopyIcon } from "../../assets/icons/questionsPage/CopyIcon";
import Branching from "../../assets/icons/questionsPage/branching";
import Clue from "../../assets/icons/questionsPage/clue";
import { HideIcon } from "../../assets/icons/questionsPage/hideIcon";
import SettingIcon from "../../assets/icons/questionsPage/settingIcon";
import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { enqueueSnackbar } from "notistack";
import { useQuestionsStore } from "@root/questions/store";
import { updateOpenedModalSettingsId } from "@root/uiTools/actions";
import { updateRootContentId } from "@root/quizes/actions";
import { useUiTools } from "@root/uiTools/store";
import { useState } from "react";
import { updateSomeWorkBackend } from "@root/uiTools/actions";
import { DeleteFunction } from "@utils/deleteFunc";
import { useState } from "react";
import { CopyIcon } from "../../../assets/icons/questionsPage/CopyIcon";
import Branching from "../../../assets/icons/questionsPage/branching";
import SettingIcon from "../../../assets/icons/questionsPage/settingIcon";
import type { AnyTypedQuizQuestion } from "../../../model/questionTypes/shared";
interface Props {
switchState: string;
@ -213,7 +199,7 @@ export default function ButtonsOptions({
setOpenDelete(true);
} else {
deleteQuestionWithTimeout(question.id, () =>
DeleteFunction(questions, question, quiz),
DeleteFunction(question),
);
}
}}
@ -258,7 +244,7 @@ export default function ButtonsOptions({
sx={{ minWidth: "150px" }}
onClick={() => {
deleteQuestionWithTimeout(question.id, () =>
DeleteFunction(questions, question, quiz),
DeleteFunction(question),
);
}}
>

@ -1,13 +1,10 @@
import { DoubleArrowRight } from "@icons/questionsPage/DoubleArrowRight";
import { DoubleTick } from "@icons/questionsPage/DoubleTick";
import { VectorQuestions } from "@icons/questionsPage/VectorQuestions";
import { QuizQuestionVariant } from "@model/questionTypes/variant";
import { QuizQuestionVarImg } from "@model/questionTypes/varimg";
import {
Box,
Button,
IconButton,
Modal,
Tooltip,
Typography,
useMediaQuery,
useTheme,
@ -15,31 +12,20 @@ import {
import {
copyQuestion,
deleteQuestion,
updateQuestion,
clearRuleForAll,
getQuestionByContentId,
deleteQuestionWithTimeout,
} from "@root/questions/actions";
import { useQuestionsStore } from "@root/questions/store";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { updateDesireToOpenABranchingModal } from "@root/uiTools/actions";
import MiniButtonSetting from "@ui_kit/MiniButtonSetting";
import { ReallyChangingModal } from "@ui_kit/Modal/ReallyChangingModal/ReallyChangingModal";
import { DeleteFunction } from "@utils/deleteFunc";
import { useEffect, useState } from "react";
import { CopyIcon } from "../../assets/icons/questionsPage/CopyIcon";
import Branching from "../../assets/icons/questionsPage/branching";
import Clue from "../../assets/icons/questionsPage/clue";
import { DeleteIcon } from "../../assets/icons/questionsPage/deleteIcon";
import { HideIcon } from "../../assets/icons/questionsPage/hideIcon";
import ImgIcon from "../../assets/icons/questionsPage/imgIcon";
import SettingIcon from "../../assets/icons/questionsPage/settingIcon";
import { QuizQuestionVariant } from "@model/questionTypes/variant";
import { updateOpenedModalSettingsId } from "@root/questions/actions";
import { updateDesireToOpenABranchingModal } from "@root/uiTools/actions";
import { useQuestionsStore } from "@root/questions/store";
import { enqueueSnackbar } from "notistack";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { updateRootContentId } from "@root/quizes/actions";
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
import { updateSomeWorkBackend } from "@root/uiTools/actions";
import { DeleteFunction } from "@utils/deleteFunc";
interface Props {
switchState: string;
@ -337,7 +323,7 @@ export default function ButtonsOptionsAndPict({
setOpenDelete(true);
} else {
deleteQuestionWithTimeout(question.id, () =>
DeleteFunction(questions, question, quiz),
DeleteFunction(question),
);
}
}}
@ -382,7 +368,7 @@ export default function ButtonsOptionsAndPict({
sx={{ minWidth: "150px" }}
onClick={() => {
deleteQuestionWithTimeout(question.id, () =>
DeleteFunction(questions, question, quiz),
DeleteFunction(question),
);
}}
>

@ -3,13 +3,12 @@ import {
UntypedQuizQuestion,
} from "@model/questionTypes/shared";
import { Box, ListItem, Typography, useTheme } from "@mui/material";
import { cancelQuestionDeletion } from "@root/questions/actions";
import { updateEditSomeQuestion } from "@root/uiTools/actions";
import { useUiTools } from "@root/uiTools/store";
import { memo, useEffect } from "react";
import { Draggable } from "react-beautiful-dnd";
import QuestionsPageCard from "./QuestionPageCard";
import { cancelQuestionDeletion } from "@root/questions/actions";
import { updateEditSomeQuestion } from "@root/uiTools/actions";
import { useQuestionsStore } from "@root/questions/store";
import { useUiTools } from "@root/uiTools/store";
type Props = {
question: AnyTypedQuizQuestion | UntypedQuizQuestion;
@ -27,7 +26,7 @@ function DraggableListItem({
setOpenBranchingPage,
}: Props) {
const theme = useTheme();
const { editSomeQuestion } = useUiTools();
const editSomeQuestion = useUiTools((state) => state.editSomeQuestion);
useEffect(() => {
let counter = 0;

@ -13,6 +13,7 @@ import OptionsPict from "@icons/questionsPage/options_pict";
import Page from "@icons/questionsPage/page";
import RatingIcon from "@icons/questionsPage/rating";
import Slider from "@icons/questionsPage/slider";
import { QuestionType } from "@model/question/question";
import ExpandLessIcon from "@mui/icons-material/ExpandLess";
import {
Box,
@ -21,8 +22,9 @@ import {
IconButton,
InputAdornment,
Modal,
TextField as MuiTextField,
Paper,
TextField,
TextFieldProps,
Typography,
useMediaQuery,
useTheme,
@ -31,26 +33,24 @@ import {
copyQuestion,
createUntypedQuestion,
deleteQuestion,
deleteQuestionWithTimeout,
toggleExpandQuestion,
updateQuestion,
updateUntypedQuestion,
deleteQuestionWithTimeout,
} from "@root/questions/actions";
import { useRef, useState } from "react";
import { DeleteFunction } from "@utils/deleteFunc";
import { FC, useRef, useState } from "react";
import type { DraggableProvidedDragHandleProps } from "react-beautiful-dnd";
import { useDebouncedCallback } from "use-debounce";
import { ReactComponent as PlusIcon } from "../../../assets/icons/plus.svg";
import type {
AnyTypedQuizQuestion,
UntypedQuizQuestion,
} from "../../../model/questionTypes/shared";
import SwitchQuestionsPage from "../SwitchQuestionsPage";
import { ChooseAnswerModal } from "./ChooseAnswerModal";
import TypeQuestions from "../TypeQuestions";
import { QuestionType } from "@model/question/question";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { useQuestionsStore } from "@root/questions/store";
import { DeleteFunction } from "@utils/deleteFunc";
import { ChooseAnswerModal } from "./ChooseAnswerModal";
const TextField = MuiTextField as unknown as FC<TextFieldProps>;
interface Props {
question: AnyTypedQuizQuestion | UntypedQuizQuestion;
@ -71,7 +71,6 @@ export default function QuestionsPageCard({
}: Props) {
const maxLengthTextField = 225;
const { questions } = useQuestionsStore();
const [plusVisible, setPlusVisible] = useState<boolean>(false);
const [open, setOpen] = useState<boolean>(false);
@ -82,16 +81,15 @@ export default function QuestionsPageCard({
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
const isMobile = useMediaQuery(theme.breakpoints.down(790));
const anchorRef = useRef(null);
const quiz = useCurrentQuiz();
const setTitle = useDebouncedCallback((title) => {
const setTitle = (title: string) => {
const updateQuestionFn =
question.type === null ? updateUntypedQuestion : updateQuestion;
updateQuestionFn(question.id, (question) => {
question.title = title;
});
}, 200);
};
const handleInputFocus = () => {
setIsTextFieldtActive(true);
@ -134,11 +132,9 @@ export default function QuestionsPageCard({
>
<TextField
id="questionTitle"
defaultValue={question.title}
value={question.title}
placeholder={"Заголовок вопроса"}
onChange={({ target }: { target: HTMLInputElement }) =>
setTitle(target.value || " ")
}
onChange={({ target }) => setTitle(target.value || " ")}
onFocus={handleInputFocus}
onBlur={handleInputBlur}
inputProps={{
@ -303,7 +299,7 @@ export default function QuestionsPageCard({
setOpenDelete(true);
} else {
deleteQuestionWithTimeout(question.id, () =>
DeleteFunction(questions, question, quiz),
DeleteFunction(question),
);
}
}}
@ -350,7 +346,7 @@ export default function QuestionsPageCard({
sx={{ minWidth: "150px" }}
onClick={() => {
deleteQuestionWithTimeout(question.id, () =>
DeleteFunction(questions, question, quiz),
DeleteFunction(question),
);
}}
>

@ -15,18 +15,14 @@ import {
} from "@root/questions/actions";
import CustomTextField from "@ui_kit/CustomTextField";
import { CopyIcon } from "@icons/questionsPage/CopyIcon";
import { DeleteIcon } from "@icons/questionsPage/deleteIcon";
import { updateDesireToOpenABranchingModal } from "@root/uiTools/actions";
import { MediaSelectionAndDisplay } from "@ui_kit/MediaSelectionAndDisplay";
import { DeleteFunction } from "@utils/deleteFunc";
import { useState } from "react";
import { useDebouncedCallback } from "use-debounce";
import type { QuizQuestionPage } from "../../../model/questionTypes/page";
import ButtonsOptions from "../ButtonsOptions";
import SwitchPageOptions from "./switchPageOptions";
import { MediaSelectionAndDisplay } from "@ui_kit/MediaSelectionAndDisplay";
import { CopyIcon } from "@icons/questionsPage/CopyIcon";
import { DeleteFunction } from "@utils/deleteFunc";
import { DeleteIcon } from "@icons/questionsPage/deleteIcon";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { useQuestionsStore } from "@root/questions/store";
import { updateDesireToOpenABranchingModal } from "@root/uiTools/actions";
type Props = {
disableInput?: boolean;
@ -50,8 +46,6 @@ export default function PageOptions({ disableInput, question }: Props) {
});
}, 200);
const quiz = useCurrentQuiz();
const { questions } = useQuestionsStore.getState();
const [openDelete, setOpenDelete] = useState<boolean>(false);
const openedModal = () => {
@ -126,7 +120,7 @@ export default function PageOptions({ disableInput, question }: Props) {
setOpenDelete(true);
} else {
deleteQuestionWithTimeout(question.id, () =>
DeleteFunction(questions, question, quiz),
DeleteFunction(question),
);
}
}}
@ -171,7 +165,7 @@ export default function PageOptions({ disableInput, question }: Props) {
sx={{ minWidth: "150px" }}
onClick={() => {
deleteQuestionWithTimeout(question.id, () =>
DeleteFunction(questions, question, quiz),
DeleteFunction(question),
);
}}
>

@ -1,14 +1,9 @@
import { useEffect, useLayoutEffect } from "react";
import { Box, useMediaQuery, useTheme } from "@mui/material";
import { deleteTimeoutedQuestions } from "@utils/deleteTimeoutedQuestions";
import { useCallback } from "react";
import { BranchingMap } from "./BranchingMap";
import { DraggableList } from "./DraggableList";
import { SwitchBranchingPanel } from "./SwitchBranchingPanel";
import { BranchingMap } from "./BranchingMap";
import { useQuestionsStore } from "@root/questions/store";
import { useUiTools } from "@root/uiTools/store";
import { useQuestions } from "@root/questions/hooks";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { deleteTimeoutedQuestions } from "@utils/deleteTimeoutedQuestions";
interface Props {
openBranchingPage: boolean;
@ -21,17 +16,15 @@ export const QuestionSwitchWindowTool = ({
setOpenBranchingPage,
widthMain,
}: Props) => {
const { questions } = useQuestionsStore.getState();
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down(600));
const quiz = useCurrentQuiz();
const openBranchingPageHC = () => {
const openBranchingPageHC = useCallback(() => {
if (!openBranchingPage) {
deleteTimeoutedQuestions(questions, quiz);
deleteTimeoutedQuestions();
}
setOpenBranchingPage(!openBranchingPage);
};
}, [openBranchingPage, setOpenBranchingPage]);
return (
<Box

@ -3,7 +3,7 @@ import Sidebar from "@ui_kit/Sidebar/Sidebar";
import Box from "@mui/material/Box";
import { useTheme, useMediaQuery, IconButton } from "@mui/material";
import HeaderFull from "@ui_kit/Header/HeaderFull";
import { useEffect, useRef, useState } from "react";
import { useCallback, useEffect, useRef, useState } from "react";
import { SidebarMobile } from "../ui_kit/Sidebar/SidebarMobile";
import { setShowConfirmLeaveModal } from "@root/uiTools/actions";
import { setCurrentStep, setQuizes } from "@root/quizes/actions";
@ -73,12 +73,12 @@ export default function Main({ sidebar, header, footer, Page }: Props) {
const [nextStep, setNextStep] = useState<number>(0);
const [openBranchingPage, setOpenBranchingPage] = useState<boolean>(false);
const openBranchingPageHC = () => {
const openBranchingPageHC = useCallback(() => {
if (!openBranchingPage) {
deleteTimeoutedQuestions(questions, quiz);
deleteTimeoutedQuestions();
}
setOpenBranchingPage((old) => !old);
};
}, [openBranchingPage, setOpenBranchingPage]);
const isConditionMet =
[1].includes(currentStep) && quizConfig?.type !== "form";

@ -34,3 +34,11 @@ export function useCurrentQuiz() {
return quiz;
}
export function getCurrentQuiz() {
const { quizes, editQuizId } = useQuizStore.getState();
const quiz = quizes.find((q) => q.backendId === editQuizId);
return quiz;
}

@ -6,15 +6,17 @@ import {
getQuestionByContentId,
updateQuestion,
} from "@root/questions/actions";
import { useQuestionsStore } from "@root/questions/store";
import { updateRootContentId } from "@root/quizes/actions";
import { getCurrentQuiz } from "@root/quizes/hooks";
//Всё здесь нужно сделать последовательно. И пусть весь мир ждёт.
export const DeleteFunction = async (
questions: any,
question: any,
quiz: any,
) => {
export const DeleteFunction = async (question: any) => {
const questions = useQuestionsStore.getState().questions;
const quiz = getCurrentQuiz();
if (!quiz) throw new Error("Quiz is null");
if (question.type !== null) {
if (question.content.rule.parentId === "root") {
//удалить из стора root и очистить rule всем вопросам

@ -1,17 +1,11 @@
import {
AnyTypedQuizQuestion,
UntypedQuizQuestion,
} from "@model/questionTypes/shared";
import { Quiz } from "@model/quiz/quiz";
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
import { useQuestionsStore } from "@root/questions/store";
import { updateSomeWorkBackend } from "@root/uiTools/actions";
import { DeleteFunction } from "@utils/deleteFunc";
type allQuestionsTypes = AnyTypedQuizQuestion | UntypedQuizQuestion;
export const deleteTimeoutedQuestions = async () => {
const questions = useQuestionsStore.getState().questions;
export const deleteTimeoutedQuestions = async (
questions: allQuestionsTypes[],
quiz: Quiz | undefined,
) => {
const questionsForDeletion = questions.filter(
({ type, deleted }) => type && type !== "result" && deleted,
) as AnyTypedQuizQuestion[];
@ -19,9 +13,7 @@ export const deleteTimeoutedQuestions = async (
updateSomeWorkBackend(true);
await Promise.allSettled(
questionsForDeletion.map((question) =>
DeleteFunction(questions, question, quiz),
),
questionsForDeletion.map((question) => DeleteFunction(question)),
);
updateSomeWorkBackend(false);