mirror of
https://github.com/emilybache/GildedRose-Refactoring-Kata.git
synced 2026-02-16 06:51:27 +00:00
refactor: implement readable structure
This commit is contained in:
parent
6818eda220
commit
87f61b8793
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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>
|
||||
);
|
||||
})}
|
||||
20
react-typescript/src/components/StoreProvider.tsx
Normal file
20
react-typescript/src/components/StoreProvider.tsx
Normal 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;
|
||||
8
react-typescript/src/hooks/useAllItems.ts
Normal file
8
react-typescript/src/hooks/useAllItems.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { useStore } from "../model";
|
||||
|
||||
function useAllItems() {
|
||||
const { state } = useStore();
|
||||
return { items: state.items };
|
||||
}
|
||||
|
||||
export default useAllItems;
|
||||
12
react-typescript/src/hooks/useDayLoader.ts
Normal file
12
react-typescript/src/hooks/useDayLoader.ts
Normal 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;
|
||||
@ -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;
|
||||
@ -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>
|
||||
);
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
const enum EEvents {
|
||||
NEXT_DAY = 'NEXT_DAY',
|
||||
}
|
||||
|
||||
export default EEvents;
|
||||
@ -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;
|
||||
@ -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;
|
||||
11
react-typescript/src/model/reducer/getItemOnNextDay.ts
Normal file
11
react-typescript/src/model/reducer/getItemOnNextDay.ts
Normal 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;
|
||||
@ -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;
|
||||
}
|
||||
|
||||
42
react-typescript/src/model/reducer/updateQuality.ts
Normal file
42
react-typescript/src/model/reducer/updateQuality.ts
Normal 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;
|
||||
13
react-typescript/src/model/reducer/updateSellIn.ts
Normal file
13
react-typescript/src/model/reducer/updateSellIn.ts
Normal 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;
|
||||
10
react-typescript/src/model/storeContext.ts
Normal file
10
react-typescript/src/model/storeContext.ts
Normal 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;
|
||||
@ -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);
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
const qualityCalculator = (item: any) => {
|
||||
return 1;
|
||||
}
|
||||
|
||||
export default qualityCalculator;
|
||||
@ -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
|
||||
};
|
||||
|
||||
5
react-typescript/src/utils/clamp.ts
Normal file
5
react-typescript/src/utils/clamp.ts
Normal 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;
|
||||
Loading…
Reference in New Issue
Block a user