refactor: implement readable structure

This commit is contained in:
sirlolcat 2022-06-05 10:11:38 +04:30
parent 6818eda220
commit 87f61b8793
19 changed files with 165 additions and 126 deletions

View File

@ -1,10 +1,13 @@
import useDayLoader from '../hooks/useDayLoader';
import './App.css';
import Items from './Item';
import Items from './Items';
function App() {
const { loadNextDay } = useDayLoader();
return (
<div className="App">
<Items />
<button onClick={() => loadNextDay()}></button>
</div>
);
}

View File

@ -1,15 +1,15 @@
import useItem from "../hooks/useItem";
import useAllItems from "../hooks/useAllItems";
import { TItem } from "../types"
function Items() {
const { items, updateItem } = useItem();
const { items } = useAllItems();
return (
<div className="Item">
<ul>
{items.map((item: TItem) => {
return (
<li key={item.name}>
Name: {item.name} Quality: {item.quality} SellIn: {item.sellIn} <button onClick={() => updateItem(item)}></button>
Name: {item.name} Quality: {item.quality} SellIn: {item.sellIn}
</li>
);
})}

View File

@ -0,0 +1,20 @@
import { useReducer } from "react";
import initialState from "../model/initialState";
import reducer from "../model/reducer";
import StoreContext from "../model/storeContext";
type TProps = {
children: JSX.Element;
}
function StoreProvider({ children }: TProps): JSX.Element {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<StoreContext.Provider value={{ state, dispatch }}>
{ children }
</StoreContext.Provider>
);
}
export default StoreProvider;

View File

@ -0,0 +1,8 @@
import { useStore } from "../model";
function useAllItems() {
const { state } = useStore();
return { items: state.items };
}
export default useAllItems;

View File

@ -0,0 +1,12 @@
import { useStore } from "../model";
import {EActionTypes} from "../types";
function useDayLoader() {
const { dispatch } = useStore();
const loadNextDay = () => {
dispatch({ type: EActionTypes.NEXT_DAY });
};
return { loadNextDay };
}
export default useDayLoader;

View File

@ -1,22 +1,10 @@
import { useStore } from "../model";
import EEvents from "../model/EEvents";
import { TItem } from "../types"
function useItem() {
const { state, dispatch } = useStore();
return {
items: state,
updateItem: (item: TItem):void => {
dispatch({
type: EEvents.NEXT_DAY,
payload: {
name: item.name
}
});
}
};
import {TItem} from "../types";
function useItem(item: TItem): TItem {
const { state } = useStore();
const itemFound = state.items.find(target => target.name.toLowerCase().includes(item.name.toLowerCase()));
return itemFound === undefined ? item : itemFound;
}
export default useItem;

View File

@ -2,12 +2,15 @@ import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './components/App';
import StoreProvider from './components/StoreProvider';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<App />
<StoreProvider>
<App />
</StoreProvider>
</React.StrictMode>
);

View File

@ -1,5 +0,0 @@
const enum EEvents {
NEXT_DAY = 'NEXT_DAY',
}
export default EEvents;

View File

@ -1,48 +0,0 @@
import { TItem } from "../../types"
function calculateQuality(state: TItem): number {
const sellInAmount = state.sellIn;
let calculatedQuality = state.quality;
let degradeRate = 1;
let qualityIdentifier = 1;
if(state.name.includes("Sulfuras")) {
return 80;
}
if(state.name.includes("Aged Brie")) {
qualityIdentifier = -1;
}
if(state.name.includes("Backstage passes")) {
qualityIdentifier = -1;
if(sellInAmount <= 10 && sellInAmount > 6) {
degradeRate = 2;
}
if(sellInAmount <= 6 && sellInAmount > 0) {
degradeRate = 3;
}
if(sellInAmount <= 0) {
return 0;
}
}
if(state.isConjured || state.sellIn < 0) {
degradeRate = 2;
}
calculatedQuality = calculatedQuality - (qualityIdentifier * degradeRate);
if(calculatedQuality > 50) {
return 50;
}
if(calculatedQuality < 0) {
return 0;
}
return calculatedQuality;
}
export default calculateQuality;

View File

@ -1,11 +0,0 @@
function updateSellIn (state: any): number {
let calculatedSellIn = state.sellIn;
if(state.name.includes("Sulfuras")) {
return calculatedSellIn;
}
return calculatedSellIn - 1;
}
export default updateSellIn;

View File

@ -0,0 +1,11 @@
import { TItem } from '../../types';
import updateQuality from './updateQuality';
import updateSellIn from './updateSellIn';
function getItemOnNextDay(item: TItem): TItem {
const itemWithUpdatedSellIn = updateSellIn(item);
const itemWithUpdatedQuality = updateQuality(itemWithUpdatedSellIn);
return itemWithUpdatedQuality;
}
export default getItemOnNextDay;

View File

@ -1,26 +1,9 @@
import { TItem } from '../../types';
import EEvents from '../EEvents';
import calculateQuality from '././calculateQuality';
import calculateSellIn from '././calculateSellIn';
import { TState, TAction, EActionTypes } from '../../types';
import getItemOnNextDay from './getItemOnNextDay';
function updateQuality(item: TItem): TItem {
const calculatedQuality = calculateQuality(item);
return { ...item, quality: calculatedQuality };
}
function updateSellIn(item: TItem): TItem {
const calculatedSellIn = calculateSellIn(item);
return { ...item, sellIn: calculatedSellIn };
}
function reducer(state: TItem[], action: any): TItem[] {
if(action.type === EEvents.NEXT_DAY) {
return state.map(item => {
if(item.name.includes(action.payload.name)) {
return updateQuality(updateSellIn(item));
}
return item;
});
function reducer(state: TState, action: TAction): TState {
if(action.type === EActionTypes.NEXT_DAY) {
return { ...state, items: state.items.map(getItemOnNextDay) };
}
return state;
}

View File

@ -0,0 +1,42 @@
import { TItem } from "../../types"
import clamp from "../../utils/clamp";
function updateQuality(item: TItem): TItem {
const sellInAmount = item.sellIn;
let calculatedQuality = item.quality;
let degradeRate = 1;
let qualityIdentifier = 1;
if(item.name.toLowerCase().includes('sulfuras')) {
return { ...item, quality: 80 };
}
if(item.name.toLowerCase().includes('aged brie')) {
qualityIdentifier = -1;
}
if(item.name.toLowerCase().includes('backstage passes')) {
qualityIdentifier = -1;
if(sellInAmount <= 10 && sellInAmount > 6) {
degradeRate = 2;
}
if(sellInAmount <= 6 && sellInAmount > 0) {
degradeRate = 3;
}
if(sellInAmount <= 0) {
return { ...item, quality: 0 };
}
}
if(item.isConjured || item.sellIn < 0) {
degradeRate = 2;
}
calculatedQuality = calculatedQuality - (qualityIdentifier * degradeRate);
calculatedQuality = clamp(calculatedQuality, 0, 50);
return { ...item, quality: calculatedQuality };
}
export default updateQuality;

View File

@ -0,0 +1,13 @@
import { TItem } from '../../types';
function updateSellIn (state: TItem): TItem {
let calculatedSellIn = state.sellIn;
if(state.name.toLowerCase().includes('sulfuras')) {
return {...state, sellIn: calculatedSellIn};
}
return { ...state, sellIn: calculatedSellIn - 1 };
}
export default updateSellIn;

View File

@ -0,0 +1,10 @@
import { createContext } from "react";
import { TContext } from "../types";
import initialState from "./initialState";
const StoreContext = createContext<TContext>({
state: initialState,
dispatch: () => {},
})
export default StoreContext;

View File

@ -1,12 +1,6 @@
import { useReducer } from "react";
import initialState from "./initialState";
import reducer from "./reducer";
import { useContext } from "react";
import StoreContext from "./storeContext";
export default function useStore() {
const [state, dispatch] = useReducer(reducer, initialState.items);
return {
state,
dispatch
}
return useContext(StoreContext);
}

View File

@ -1,5 +0,0 @@
const qualityCalculator = (item: any) => {
return 1;
}
export default qualityCalculator;

View File

@ -5,11 +5,27 @@ type TItem = {
isConjured: boolean
}
type TGildedRose = {
type TState = {
items: TItem[]
};
type TAction = {
type: EActionTypes,
payload?: any
}
type TContext = {
state: TState,
dispatch: (action: TAction) => void
}
export enum EActionTypes {
NEXT_DAY = 'NEXT_DAY',
}
export type {
TItem,
TGildedRose
}
TState,
TAction,
TContext
};

View File

@ -0,0 +1,5 @@
function clamp(number: number, min: number, max: number): number {
return Math.min(Math.max(number, min), max);
}
export default clamp;