mirror of
https://github.com/emilybache/GildedRose-Refactoring-Kata.git
synced 2026-02-17 15:31: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 './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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
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 { 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;
|
||||||
@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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 { 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;
|
||||||
}
|
}
|
||||||
|
|||||||
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 { 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -1,5 +0,0 @@
|
|||||||
const qualityCalculator = (item: any) => {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default qualityCalculator;
|
|
||||||
@ -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
|
||||||
|
};
|
||||||
|
|||||||
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