Skip to content

Commit

Permalink
Use targeted useAppSelector to avoid sidebar rerender loop (#2834)
Browse files Browse the repository at this point in the history
  • Loading branch information
imnasnainaec authored Dec 12, 2023
1 parent e7af364 commit d69f7d1
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,25 @@ import {
flagWord,
setVern,
} from "goals/MergeDuplicates/Redux/MergeDupsActions";
import { MergeTreeState } from "goals/MergeDuplicates/Redux/MergeDupsReduxTypes";
import { useAppDispatch } from "types/hooks";
import { StoreState } from "types";
import { useAppDispatch, useAppSelector } from "types/hooks";
import theme from "types/theme";
import { TypographyWithFont } from "utilities/fontComponents";

interface DropWordProps {
mergeState: MergeTreeState;
wordId: string;
}

export default function DropWord(props: DropWordProps): ReactElement {
const dispatch = useAppDispatch();
const data = useAppSelector(
(state: StoreState) => state.mergeDuplicateGoal.data
);
const treeWord = useAppSelector(
(state: StoreState) => state.mergeDuplicateGoal.tree.words[props.wordId]
);
const { t } = useTranslation();

const treeWord = props.mergeState.tree.words[props.wordId];
const data = props.mergeState.data;
let protectedWithOneChild = false;
const verns: string[] = [];
if (treeWord) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,30 @@ import SenseCardContent from "goals/MergeDuplicates/MergeDupsStep/SenseCardConte
import {
MergeTreeReference,
MergeTreeSense,
Sidebar,
} from "goals/MergeDuplicates/MergeDupsTreeTypes";
import { StoreState } from "types";
import { useAppSelector } from "types/hooks";
import theme from "types/theme";

interface SidebarDragSenseProps {
sidebar: Sidebar;
sense: MergeTreeSense;
index: number;
}

export default function SidebarDragSense(
props: SidebarDragSenseProps
): ReactElement {
const ref: MergeTreeReference = {
wordId: props.sidebar.wordId,
mergeSenseId: props.sidebar.mergeSenseId,
order: props.index,
};
const draggableId = useAppSelector((state: StoreState) => {
const { mergeSenseId, wordId } = state.mergeDuplicateGoal.tree.sidebar;
const order = props.index;
const ref: MergeTreeReference = { wordId, mergeSenseId, order };
return JSON.stringify(ref);
});

return (
<Draggable
key={props.sense.guid}
draggableId={JSON.stringify(ref)}
draggableId={draggableId}
index={props.index}
isDragDisabled={props.sense.protected}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,25 @@ import { ReactElement } from "react";
import { Droppable } from "react-beautiful-dnd";

import SidebarDragSense from "goals/MergeDuplicates/MergeDupsStep/MergeDragDrop/SidebarDragSense";
import {
MergeTreeSense,
Sidebar,
} from "goals/MergeDuplicates/MergeDupsTreeTypes";
import { MergeTreeSense } from "goals/MergeDuplicates/MergeDupsTreeTypes";
import { setSidebar } from "goals/MergeDuplicates/Redux/MergeDupsActions";
import { useAppDispatch } from "types/hooks";
import { StoreState } from "types";
import { useAppDispatch, useAppSelector } from "types/hooks";

interface SidebarDropProps {
sidebar: Sidebar;
vernacular: string;
}

export default function SidebarDrop(props: SidebarDropProps): ReactElement {
export default function SidebarDrop(): ReactElement {
const dispatch = useAppDispatch();
const sidebar = useAppSelector(
(state: StoreState) => state.mergeDuplicateGoal.tree.sidebar
);
const vernacular = useAppSelector((state: StoreState) => {
const tree = state.mergeDuplicateGoal.tree;
return tree.words[tree.sidebar.wordId]?.vern;
});

return (
<Droppable
droppableId={`${props.sidebar.wordId} ${props.sidebar.mergeSenseId}`}
key={props.sidebar.mergeSenseId}
droppableId={`${sidebar.wordId} ${sidebar.mergeSenseId}`}
key={sidebar.mergeSenseId}
>
{(providedDroppable): ReactElement => (
<div
Expand All @@ -37,14 +37,9 @@ export default function SidebarDrop(props: SidebarDropProps): ReactElement {
>
<ArrowForwardIos />
</IconButton>
<Typography variant="h5">{props.vernacular}</Typography>
{props.sidebar.senses.map((sense: MergeTreeSense, index: number) => (
<SidebarDragSense
key={index}
index={index}
sidebar={props.sidebar}
sense={sense}
/>
<Typography variant="h5">{vernacular}</Typography>
{sidebar.senses.map((sense: MergeTreeSense, index: number) => (
<SidebarDragSense key={index} index={index} sense={sense} />
))}
{providedDroppable.placeholder}
</div>
Expand Down
44 changes: 22 additions & 22 deletions src/goals/MergeDuplicates/MergeDupsStep/MergeDragDrop/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,25 @@ export const trashId = "trash-drop";

export default function MergeDragDrop(): ReactElement {
const dispatch = useAppDispatch();
const mergeState = useAppSelector(
(state: StoreState) => state.mergeDuplicateGoal
const sidebarOpen = useAppSelector(
(state: StoreState) =>
state.mergeDuplicateGoal.tree.sidebar.senses.length > 1
);
const sidebarProtected = useAppSelector((state: StoreState) => {
const senses = state.mergeDuplicateGoal.tree.sidebar.senses;
return senses.length && senses[0].protected;
});
const words = useAppSelector(
(state: StoreState) => state.mergeDuplicateGoal.tree.words
);

const [senseToDelete, setSenseToDelete] = useState<string>("");
const { t } = useTranslation();

const sidebar = mergeState.tree.sidebar;
const treeWords = mergeState.tree.words;

function handleDrop(res: DropResult): void {
const src: MergeTreeReference = JSON.parse(res.draggableId);
const srcWordId = res.source.droppableId;
const srcWord = treeWords[srcWordId];
const srcWord = words[srcWordId];
if (srcWord?.protected && Object.keys(srcWord.sensesGuids).length === 1) {
// Case 0: The final sense of a protected word cannot be moved.
return;
Expand Down Expand Up @@ -89,9 +95,7 @@ export default function MergeDragDrop(): ReactElement {
const destOrder = res.destination.index;
if (
src.order === destOrder ||
(destOrder === 0 &&
src.order !== undefined &&
sidebar.senses[0].protected)
(destOrder === 0 && src.order !== undefined && sidebarProtected)
) {
// If the sense wasn't moved or was moved within the sidebar above a protected sense, do nothing.
return;
Expand All @@ -107,31 +111,27 @@ export default function MergeDragDrop(): ReactElement {
}

function renderSidebar(): ReactElement {
if (sidebar.senses.length <= 1) {
return <div />;
}
return (
return sidebarOpen ? (
<Drawer
anchor="right"
variant="persistent"
open={sidebar.senses.length > 1}
open={sidebarOpen}
SlideProps={{
style: {
height: `calc(100% - ${appBarHeight}px)`,
top: appBarHeight,
},
}}
>
<SidebarDrop
sidebar={sidebar}
vernacular={treeWords[sidebar.wordId]?.vern}
/>
<SidebarDrop />
</Drawer>
) : (
<div />
);
}

const newId = v4();
const colCount = Object.keys(treeWords).length + 1; // +1 for extra empty word.
const colCount = Object.keys(words).length + 1; // +1 for extra empty word.

// This prevents things from moving when a draggable is dragged over the trash.
const trashPlaceholderStyle: CSSProperties = {
Expand All @@ -157,16 +157,16 @@ export default function MergeDragDrop(): ReactElement {
</Grid>
<Grid item sm={11} xs={10 /* Allow trash icon more space. */}>
<ImageList rowHeight="auto" cols={colCount} style={{ width: "90vw" }}>
{Object.keys(treeWords).map((key) => (
{Object.keys(words).map((key) => (
<ImageListItem
key={key}
style={{ height: "70vh", margin: theme.spacing(1) }}
>
<DropWord mergeState={mergeState} wordId={key} />
<DropWord wordId={key} />
</ImageListItem>
))}
<ImageListItem key={newId} style={{ margin: theme.spacing(1) }}>
<DropWord mergeState={mergeState} wordId={newId} />
<DropWord wordId={newId} />
</ImageListItem>
{renderSidebar()}
<CancelConfirmDialog
Expand Down

0 comments on commit d69f7d1

Please sign in to comment.