Refactor AreasContextProvider: Add context export, custom hook, PropTypes validation, and error handling

- Added `AreasContext` export and created `useAreasContext` custom hook for safer and easier context access.
 - Integrated `PropTypes` to validate `children` prop in `AreasContextProvider` component.
 - Refactored `addArea` function by breaking it into `addExistingArea` and `addNewArea` for improved readability and maintainability.
 - Added error handling in `deleteArea` function to prevent operations with invalid IDs.
 - Extracted logic to clear selected element into a separate function for better separation of concerns.
 - Ensured proper default export for `AreasContextProvider` to facilitate correct imports.

 These changes improve code robustness, readability, and type safety in the JavaScript environment.
This commit is contained in:
alphazee09 2024-08-08 19:42:50 +04:00
parent 2f26d12f6a
commit d938f72e1d
3 changed files with 108 additions and 92 deletions

51
package-lock.json generated
View File

@ -43,7 +43,7 @@
"@types/react-dom": "^18.2.17",
"@vitejs/plugin-react": "^4.2.1",
"autoprefixer": "^10.4.16",
"eslint": "^8.55.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
@ -1111,22 +1111,25 @@
}
},
"node_modules/@eslint/js": {
"version": "8.56.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz",
"integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==",
"version": "8.57.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
"integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.11.13",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
"integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==",
"version": "0.11.14",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
"integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
"deprecated": "Use @eslint/config-array instead",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@humanwhocodes/object-schema": "^2.0.1",
"debug": "^4.1.1",
"@humanwhocodes/object-schema": "^2.0.2",
"debug": "^4.3.1",
"minimatch": "^3.0.5"
},
"engines": {
@ -1147,10 +1150,12 @@
}
},
"node_modules/@humanwhocodes/object-schema": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz",
"integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==",
"dev": true
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
"integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
"deprecated": "Use @eslint/object-schema instead",
"dev": true,
"license": "BSD-3-Clause"
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.3",
@ -2317,9 +2322,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001570",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001570.tgz",
"integrity": "sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw==",
"version": "1.0.30001651",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz",
"integrity": "sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==",
"dev": true,
"funding": [
{
@ -2334,7 +2339,8 @@
"type": "github",
"url": "https://github.com/sponsors/ai"
}
]
],
"license": "CC-BY-4.0"
},
"node_modules/canvg": {
"version": "3.0.10",
@ -2873,16 +2879,17 @@
}
},
"node_modules/eslint": {
"version": "8.56.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz",
"integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==",
"version": "8.57.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
"integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
"@eslint/eslintrc": "^2.1.4",
"@eslint/js": "8.56.0",
"@humanwhocodes/config-array": "^0.11.13",
"@eslint/js": "8.57.0",
"@humanwhocodes/config-array": "^0.11.14",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
"@ungap/structured-clone": "^1.2.0",

View File

@ -45,7 +45,7 @@
"@types/react-dom": "^18.2.17",
"@vitejs/plugin-react": "^4.2.1",
"autoprefixer": "^10.4.16",
"eslint": "^8.55.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",

View File

@ -1,12 +1,23 @@
import { createContext, useState } from "react";
import { Action, ObjectType, defaultBlue } from "../data/constants";
import { useUndoRedo, useTransform, useSelect } from "../hooks";
import { Toast } from "@douyinfe/semi-ui";
import { useTranslation } from "react-i18next";
import React, { createContext, useState, ReactNode, useContext } from 'react';
import PropTypes from 'prop-types';
import { Action, ObjectType, defaultBlue } from '../data/constants';
import { useUndoRedo, useTransform, useSelect } from '../hooks';
import { Toast } from '@douyinfe/semi-ui';
import { useTranslation } from 'react-i18next';
export const AreasContext = createContext(null);
// Define context
export const AreasContext = createContext(null); // Ensure export is here
export default function AreasContextProvider({ children }) {
export const useAreasContext = () => {
const context = useContext(AreasContext);
if (!context) {
throw new Error('useAreasContext must be used within an AreasContextProvider');
}
return context;
};
// AreasContextProvider component
const AreasContextProvider = ({ children }) => {
const { t } = useTranslation();
const [areas, setAreas] = useState([]);
const { transform } = useTransform();
@ -15,15 +26,27 @@ export default function AreasContextProvider({ children }) {
const addArea = (data, addToHistory = true) => {
if (data) {
setAreas((prev) => {
const temp = prev.slice();
temp.splice(data.id, 0, data);
return temp.map((t, i) => ({ ...t, id: i }));
});
addExistingArea(data);
} else {
addNewArea();
}
if (addToHistory) {
addActionToHistory(Action.ADD, ObjectType.AREA, t("add_area"));
}
};
const addExistingArea = (data) => {
setAreas(prev => {
const updatedAreas = [...prev];
updatedAreas.splice(data.id, 0, data);
return updatedAreas.map((area, i) => ({ ...area, id: i }));
});
};
const addNewArea = () => {
const width = 200;
const height = 200;
setAreas((prev) => [
setAreas(prev => [
...prev,
{
id: prev.length,
@ -35,66 +58,52 @@ export default function AreasContextProvider({ children }) {
color: defaultBlue,
},
]);
}
if (addToHistory) {
setUndoStack((prev) => [
...prev,
{
action: Action.ADD,
element: ObjectType.AREA,
message: t("add_area"),
},
]);
setRedoStack([]);
}
};
const deleteArea = (id, addToHistory = true) => {
if (id < 0 || id >= areas.length) return; // Handle invalid ID
if (addToHistory) {
Toast.success(t("area_deleted"));
setUndoStack((prev) => [
...prev,
{
action: Action.DELETE,
element: ObjectType.AREA,
data: areas[id],
message: t("delete_area", areas[id].name),
},
]);
setRedoStack([]);
addActionToHistory(Action.DELETE, ObjectType.AREA, t("delete_area", areas[id].name), areas[id]);
}
setAreas((prev) =>
prev.filter((e) => e.id !== id).map((e, i) => ({ ...e, id: i })),
);
setAreas(prev => {
const filteredAreas = prev.filter(e => e.id !== id);
return filteredAreas.map((e, i) => ({ ...e, id: i }));
});
if (id === selectedElement.id) {
setSelectedElement((prev) => ({
...prev,
clearSelectedElement();
}
};
const clearSelectedElement = () => {
setSelectedElement({
element: ObjectType.NONE,
id: -1,
open: false,
}));
}
});
};
const updateArea = (id, values) => {
setAreas((prev) =>
prev.map((t) => {
if (t.id === id) {
return {
...t,
...values,
setAreas(prev => prev.map(area => area.id === id ? { ...area, ...values } : area));
};
}
return t;
}),
);
const addActionToHistory = (action, element, message, data) => {
setUndoStack(prev => [
...prev,
{ action, element, message, data },
]);
setRedoStack([]);
};
return (
<AreasContext.Provider
value={{ areas, setAreas, updateArea, addArea, deleteArea }}
>
<AreasContext.Provider value={{ areas, setAreas, updateArea, addArea, deleteArea }}>
{children}
</AreasContext.Provider>
);
}
};
AreasContextProvider.propTypes = {
children: PropTypes.node.isRequired,
};
export default AreasContextProvider;