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 './App.css';
import Items from './Item'; import Items from './Items';
function App() { function App() {
const { loadNextDay } = useDayLoader();
return ( return (
<div className="App"> <div className="App">
<Items /> <Items />
<button onClick={() => loadNextDay()}></button>
</div> </div>
); );
} }

View File

@ -1,15 +1,15 @@
import useItem from "../hooks/useItem"; import useAllItems from "../hooks/useAllItems";
import { TItem } from "../types" import { TItem } from "../types"
function Items() { function Items() {
const { items, updateItem } = useItem(); const { items } = useAllItems();
return ( return (
<div className="Item"> <div className="Item">
<ul> <ul>
{items.map((item: TItem) => { {items.map((item: TItem) => {
return ( return (
<li key={item.name}> <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> </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 { useStore } from "../model";
import EEvents from "../model/EEvents"; import {TItem} from "../types";
import { TItem } from "../types"
function useItem(item: TItem): TItem {
function useItem() { const { state } = useStore();
const { state, dispatch } = useStore(); const itemFound = state.items.find(target => target.name.toLowerCase().includes(item.name.toLowerCase()));
return itemFound === undefined ? item : itemFound;
return {
items: state,
updateItem: (item: TItem):void => {
dispatch({
type: EEvents.NEXT_DAY,
payload: {
name: item.name
}
});
}
};
} }
export default useItem; export default useItem;

View File

@ -2,12 +2,15 @@ import React from 'react';
import ReactDOM from 'react-dom/client'; import ReactDOM from 'react-dom/client';
import './index.css'; import './index.css';
import App from './components/App'; import App from './components/App';
import StoreProvider from './components/StoreProvider';
const root = ReactDOM.createRoot( const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement document.getElementById('root') as HTMLElement
); );
root.render( root.render(
<React.StrictMode> <React.StrictMode>
<App /> <StoreProvider>
<App />
</StoreProvider>
</React.StrictMode> </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 { TState, TAction, EActionTypes } from '../../types';
import EEvents from '../EEvents'; import getItemOnNextDay from './getItemOnNextDay';
import calculateQuality from '././calculateQuality';
import calculateSellIn from '././calculateSellIn';
function updateQuality(item: TItem): TItem { function reducer(state: TState, action: TAction): TState {
const calculatedQuality = calculateQuality(item); if(action.type === EActionTypes.NEXT_DAY) {
return { ...item, quality: calculatedQuality }; return { ...state, items: state.items.map(getItemOnNextDay) };
}
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;
});
} }
return state; 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 { useContext } from "react";
import initialState from "./initialState"; import StoreContext from "./storeContext";
import reducer from "./reducer";
export default function useStore() { export default function useStore() {
const [state, dispatch] = useReducer(reducer, initialState.items); return useContext(StoreContext);
return {
state,
dispatch
}
} }

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 isConjured: boolean
} }
type TGildedRose = { type TState = {
items: TItem[] 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 { export type {
TItem, 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;