Skip to content

Commit

Permalink
Optimization performance for Cell, Counter, Reset
Browse files Browse the repository at this point in the history
  • Loading branch information
nickovchinnikov committed Oct 4, 2021
1 parent 7ececba commit dc11701
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 59 deletions.
52 changes: 32 additions & 20 deletions src/components/Grid/Cell.test.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import React from 'react';
import { render, screen, fireEvent, createEvent } from '@testing-library/react';

import { CellState, Coords } from '@/core/Field';
import { CellState, Cell as CellType, Coords } from '@/core/Field';

import { Cell, ClosedFrame, isActiveCell } from './Cell';
import { Cell, ClosedFrame, isActiveCell, areEqual } from './Cell';

describe('Cell component check', () => {
const coords: Coords = [1, 1];
const props = {
coords,
flagCounter: 0,
bombs: 10,
onClick: jest.fn(),
onContextMenu: jest.fn(),
};

for (let cell = CellState.empty; cell <= CellState.weakFlag; cell++) {
it('Cell renders correct', () => {
const props = {
coords,
onClick: jest.fn(),
onContextMenu: jest.fn(),
};

const { asFragment } = render(<Cell {...props}>{cell}</Cell>);

expect(asFragment()).toMatchSnapshot();
Expand All @@ -25,12 +26,6 @@ describe('Cell component check', () => {
expect(asFragment()).toMatchSnapshot();
});
it('Check prevent default contextMenu for every type of cell', () => {
const props = {
coords,
onClick: jest.fn(),
onContextMenu: jest.fn(),
};

render(<Cell {...props}>{cell}</Cell>);

const cellComp = screen.getByTestId(`${coords}`);
Expand All @@ -42,12 +37,6 @@ describe('Cell component check', () => {
});

it('onClick and onContextMenu handler should be called for active cells', () => {
const props = {
coords,
onClick: jest.fn(),
onContextMenu: jest.fn(),
};

render(<Cell {...props}>{cell}</Cell>);

const cellComp = screen.getByTestId(`${coords}`);
Expand All @@ -64,4 +53,27 @@ describe('Cell component check', () => {
}
});
}
it('Check areEqual', () => {
const prevProps = {
...props,
children: 0 as CellType,
};

expect(areEqual(prevProps, { ...prevProps })).toBe(true);

expect(areEqual(prevProps, { ...prevProps, coords: [2, 1] })).toBe(false);
expect(areEqual(prevProps, { ...prevProps, coords: [1, 2] })).toBe(false);
expect(areEqual(prevProps, { ...prevProps, coords: [2, 2] })).toBe(false);
expect(areEqual(prevProps, { ...prevProps, coords: [1, 0] })).toBe(false);

expect(areEqual(prevProps, { ...prevProps, children: 1 as CellType })).toBe(
false
);
expect(areEqual(prevProps, { ...prevProps, onClick: jest.fn() })).toBe(
false
);
expect(
areEqual(prevProps, { ...prevProps, onContextMenu: jest.fn() })
).toBe(false);
});
});
24 changes: 21 additions & 3 deletions src/components/Grid/Cell.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FC } from 'react';
import React, { FC, memo } from 'react';
import styled from '@emotion/styled';

import { Cell as CellType, Coords, CellState } from '@/core/Field';
Expand Down Expand Up @@ -27,7 +27,23 @@ export interface CellProps {
export const isActiveCell = (cell: CellType): boolean =>
[CellState.hidden, CellState.flag, CellState.weakFlag].includes(cell);

export const Cell: FC<CellProps> = ({ children, coords, ...rest }) => {
export const areEqual = (
prevProps: CellProps,
nextProps: CellProps
): boolean => {
const areEqualCoords =
prevProps.coords.filter((coord, idx) => nextProps.coords[idx] !== coord)
.length === 0;

return (
prevProps.children === nextProps.children &&
areEqualCoords &&
prevProps.onClick === nextProps.onClick &&
prevProps.onContextMenu === nextProps.onContextMenu
);
};

export const Cell: FC<CellProps> = memo(({ children, coords, ...rest }) => {
const [mouseDown, onMouseDown, onMouseUp] = useMouseDown();

const onClick = () => rest.onClick(coords);
Expand Down Expand Up @@ -55,7 +71,9 @@ export const Cell: FC<CellProps> = ({ children, coords, ...rest }) => {
};

return <ComponentsMap {...props}>{children}</ComponentsMap>;
};
}, areEqual);

Cell.displayName = 'Cell';

interface ComponentsMapProps {
children: CellType;
Expand Down
8 changes: 5 additions & 3 deletions src/components/Scoreboard/Counter.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FC } from 'react';
import React, { FC, memo } from 'react';
import styled from '@emotion/styled';

export interface CounterProps {
Expand All @@ -8,9 +8,11 @@ export interface CounterProps {
children: string;
}

export const Counter: FC<CounterProps> = ({ children }) => (
export const Counter: FC<CounterProps> = memo(({ children }) => (
<Frame>{children}</Frame>
);
));

Counter.displayName = 'Counter';

const Frame = styled.div`
display: inline-block;
Expand Down
8 changes: 5 additions & 3 deletions src/components/Scoreboard/Reset.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FC } from 'react';
import React, { FC, memo } from 'react';
import styled from '@emotion/styled';

import { useMouseDown } from '@/components/hooks/useMouseDown';
Expand All @@ -10,7 +10,7 @@ export interface ResetProps {
onReset: () => void;
}

export const Reset: FC<ResetProps> = ({ onReset }) => {
export const Reset: FC<ResetProps> = memo(({ onReset }) => {
const [mouseDown, onMouseDown, onMouseUp] = useMouseDown();

return (
Expand All @@ -23,7 +23,9 @@ export const Reset: FC<ResetProps> = ({ onReset }) => {
{mouseDown ? '😯' : '🙂'}
</Button>
);
};
});

Reset.displayName = 'Reset';

const Button = styled.button`
font-size: 1.1vw;
Expand Down
66 changes: 36 additions & 30 deletions src/modules/GameWithHooks/useGame.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from 'react';
import { useState, useCallback } from 'react';

import {
Field,
Expand Down Expand Up @@ -61,39 +61,45 @@ export const useGame = (): ReturnType => {
fieldGenerator(size, bombs / (size * size))
);

const onClick = (coords: Coords) => {
!isGameStarted && setInProgress();
try {
const [newPlayerField, isSolved] = openCell(
const onClick = useCallback(
(coords: Coords) => {
!isGameStarted && setInProgress();
try {
const [newPlayerField, isSolved] = openCell(
coords,
playerField,
gameField
);
if (isSolved) {
setGameWin();
}
setPlayerField([...newPlayerField]);
} catch (e) {
setPlayerField([...gameField]);
setGameLoose();
}
},
[isGameStarted, isGameOver, isWin, level, flagCounter]
);

const onContextMenu = useCallback(
(coords: Coords) => {
!isGameStarted && setInProgress();
const [newPlayerField, isSolved, newFlagCounter] = setFlag(
coords,
playerField,
gameField
gameField,
flagCounter,
bombs
);
setFlagCounter(newFlagCounter);
if (isSolved) {
setGameWin();
}
setPlayerField([...newPlayerField]);
} catch (e) {
setPlayerField([...gameField]);
setGameLoose();
}
};

const onContextMenu = (coords: Coords) => {
!isGameStarted && setInProgress();
const [newPlayerField, isSolved, newFlagCounter] = setFlag(
coords,
playerField,
gameField,
flagCounter,
bombs
);
setFlagCounter(newFlagCounter);
if (isSolved) {
setGameWin();
}
setPlayerField([...newPlayerField]);
};
},
[isGameStarted, isGameOver, isWin, level, flagCounter]
);

const resetHandler = ([size, bombs]: [number, number]) => {
const newGameField = fieldGenerator(size, bombs / (size * size));
Expand All @@ -109,12 +115,12 @@ export const useGame = (): ReturnType => {
setFlagCounter(0);
};

const onChangeLevel = (level: LevelNames) => {
const onChangeLevel = useCallback((level: LevelNames) => {
const newSettings = setLevel(level);
resetHandler(newSettings);
};
}, []);

const onReset = () => resetHandler([size, bombs]);
const onReset = useCallback(() => resetHandler([size, bombs]), [size, bombs]);

return {
level,
Expand Down

0 comments on commit dc11701

Please sign in to comment.