Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
minani-0621 committed Jul 24, 2024
2 parents 39cc108 + 8085b73 commit fd1adb8
Show file tree
Hide file tree
Showing 21 changed files with 335 additions and 6 deletions.
2 changes: 1 addition & 1 deletion Caecae/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
<script type="module" src="/src/App/main.tsx"></script>
</body>
</html>
File renamed without changes.
7 changes: 2 additions & 5 deletions Caecae/src/main.tsx → Caecae/src/App/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from "react";
import ReactDOM from "react-dom/client";
import "./main.css";

// 임시 React component
const App = () => {
return (
<div>
Expand All @@ -11,8 +12,4 @@ const App = () => {
);
};

ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
ReactDOM.createRoot(document.getElementById("root")!).render(<App />);
Empty file added Caecae/src/Component/.gitkeep
Empty file.
Empty file added Caecae/src/Job/.gitkeep
Empty file.
Empty file added Caecae/src/Page/.gitkeep
Empty file.
7 changes: 7 additions & 0 deletions Caecae/src/Shared/Hyundux/Actions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
interface Action {
type: string;
actionName: string;
payload?: object;
}

export default Action
28 changes: 28 additions & 0 deletions Caecae/src/Shared/Hyundux/Example_Counter/ConuntUI.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import useBind from "../Hooks/Binding.tsx";
import { action, initCountState, countReducer } from "./CountWorkFlow.tsx";
import store from "../Store.tsx";

const Counter = () => {
const state = useBind(initCountState, countReducer);

function temp1() {
store.dispatch(action.countUp());
}

function temp2() {
store.dispatch(action.getText("sdsd"));
store.dispatch(action.countDown());
}

return (
<div>
<h1>{state.text}</h1>
<div>{state.count}</div>
<br></br>
<button onClick={temp1}>up</button>
<button onClick={temp2}>down</button>
</div>
);
};

export default Counter;
64 changes: 64 additions & 0 deletions Caecae/src/Shared/Hyundux/Example_Counter/CountWorkFlow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { createState } from "../State";
import { makePayLoad } from "../Util/StoreUtil";
import Reducer from "../Reducer";
import Action from "../Actions";

const WORKFLOW_NAME = "Count";

// state type
interface CountPayLoad {
count: number;
text: string;
}

const initCountState = createState<CountPayLoad>(WORKFLOW_NAME, {
count: 0,
text: "helloWorld",
});

// define reducer
const countReducer: Reducer<CountPayLoad> = {
type: WORKFLOW_NAME,
reducer: async function reducer(state, action) {
const payLoad = state.payload;
switch (action.actionName) {
case "countUp":
return makePayLoad(state, { count: payLoad.count + 1 });
case "countDown":
return makePayLoad(state, { count: payLoad.count - 1 });
case "getText": {
const actionPayLoad = (action.payload || {}) as { text: string };
return makePayLoad(state, { text: actionPayLoad.text });
}
default:
return state;
}
},
};

// actions
const action = {
countUp: (): Action => {
return {
type: WORKFLOW_NAME,
actionName: "countUp",
};
},
countDown: (): Action => {
return {
type: WORKFLOW_NAME,
actionName: "countDown",
};
},
getText: (text: string): Action => {
return {
type: WORKFLOW_NAME,
actionName: "getText",
payload: {
text: text,
},
};
},
};

export { action, initCountState, countReducer };
16 changes: 16 additions & 0 deletions Caecae/src/Shared/Hyundux/Hooks/Binding.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useState } from 'react';
import HState from '../State';
import store from '../Store';
import Reducer from '../Reducer';


function useBind<PayLoad>(initialState: HState<PayLoad>, reducer: Reducer<PayLoad>): PayLoad {
const [state, setState] = useState<HState<PayLoad>>(initialState);
store.subscribe(state, reducer, (newState) => {
setState(newState)
});

return state.payload;
}

export default useBind;
9 changes: 9 additions & 0 deletions Caecae/src/Shared/Hyundux/Reducer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Action from "./Actions";
import State from "./State";

interface Reducer<PayLoad> {
type: string;
reducer: (state: State<PayLoad>, action: Action) => Promise<State<PayLoad>>;
}

export default Reducer;
14 changes: 14 additions & 0 deletions Caecae/src/Shared/Hyundux/State.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
interface State<T> {
type: string;
payload: T;
}

function createState<PayLoad>(type: string, payload: PayLoad): State<PayLoad> {
return {
type: type,
payload: payload
}
}

export { createState }
export default State
39 changes: 39 additions & 0 deletions Caecae/src/Shared/Hyundux/Store.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import Action from "./Actions";
import State from "./State";
import Reducer from "./Reducer";
import removeFirst from "./Util/RemoveFirst";
import replaceFirst from "./Util/ReplaceFirst";

const store: {
states: State<unknown>[],
reducers: Reducer<unknown>[],
subscribe: <PayLoad>(initState: State<PayLoad>, reducer: Reducer<PayLoad>, cb: (state: State<PayLoad>) => void) => void,
dispatch: (action: Action) => void,
publish: <PayLoad>(state: State<PayLoad>) => void,
subscribeList: Map<string, <PayLoad>(state: State<PayLoad>) => void>,
} = {
states: [],
reducers: [],
subscribeList: new Map(),
dispatch: async function (action) {
const reducer = this.reducers.filter(reducer => reducer.type == action.type)[0].reducer
const { removed, newArray } = removeFirst(this.states, (state) => state.type == action.type)
const newState = await reducer(removed, action);
// 여기서 모든것을 바로 state를 적용하는것이 아니라 이게 다른 state도 propagation하는지도 확인해야함
this.states = [...newArray, newState];
this.publish(newState);
},
publish: function (state) {
const publishedCallBack = this.subscribeList.get(state.type);
if (publishedCallBack !== undefined) {
publishedCallBack(state);
}
},
subscribe: function <PayLoad>(state: State<PayLoad>, reducer: Reducer<PayLoad>, cb: (state: State<PayLoad>) => void) {
this.states = replaceFirst(this.states, state, (element) => element.type == state.type)
this.reducers = replaceFirst(this.reducers, reducer as Reducer<unknown>, (element) => element.type == state.type)
this.subscribeList.set(state.type, cb as (state: State<unknown>) => void);
}
}

export default store
10 changes: 10 additions & 0 deletions Caecae/src/Shared/Hyundux/Util/RemoveFirst.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
function removeFirst<T>(arr: T[], predicate: (item: T) => boolean): { removed: T, newArray: T[] } {
const index = arr.findIndex(predicate);

const removed = arr[index];
const newArray = [...arr.slice(0, index), ...arr.slice(index + 1)];

return { removed, newArray };
}

export default removeFirst
15 changes: 15 additions & 0 deletions Caecae/src/Shared/Hyundux/Util/ReplaceFirst.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
function replaceFirst<Element>(
array: Element[],
newItem: Element,
condition: (item: Element) => boolean,
): Element[] {
const index = array.findIndex(condition);
if (index !== -1) {
const newArray = [...array];
newArray[index] = newItem;
return newArray;
}
return [...array, newItem];
}

export default replaceFirst
5 changes: 5 additions & 0 deletions Caecae/src/Shared/Hyundux/Util/StoreUtil.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import State from "../State";

export function makePayLoad<PayLoad>(originState: State<PayLoad>, payload: object): State<PayLoad> {
return { ...originState, payload: { ...originState.payload, ...payload } }
}
24 changes: 24 additions & 0 deletions Caecae/src/Shared/Router/Link.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React, { ReactNode, useContext, MouseEvent } from "react";
import { RouterContext } from "./Router";

interface LinkProps {
to: string;
children: ReactNode;
}

const Link: React.FC<LinkProps> = ({ to, children }) => {
const { changePath } = useContext(RouterContext);

const handleClick = (event: MouseEvent<HTMLAnchorElement>) => {
event.preventDefault();
changePath(to);
};

return (
<a href={to} onClick={handleClick}>
{children}
</a>
);
};

export default Link;
12 changes: 12 additions & 0 deletions Caecae/src/Shared/Router/Route.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React, { ReactElement } from "react";

interface RouteProps {
path: string;
element: ReactElement;
}

const Route: React.FC<RouteProps> = () => {
return null; // 실제로 렌더링하지 않음
};

export default Route;
52 changes: 52 additions & 0 deletions Caecae/src/Shared/Router/Router.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useState, createContext, ReactNode, FC, useEffect } from "react";

interface RouterProps {
children: ReactNode;
}

interface RouterContextType {
path: string;
changePath: (path: string) => void;
}

const RouterContext = createContext<RouterContextType>({
path: "",
changePath: () => undefined,
});
RouterContext.displayName = "RouterContext";

const Router: FC<RouterProps> = ({ children }) => {
const [path, setPath] = useState(window.location.pathname);

useEffect(() => {
const handleLocationChange = () => {
setPath(window.location.pathname);
};

window.addEventListener("popstate", handleLocationChange);

return () => {
window.removeEventListener("popstate", handleLocationChange);
};
}, []);

const changePath = (newPath: string) => {
if (path !== newPath) {
window.history.pushState({}, "", newPath);
setPath(newPath);
}
};

const contextValue = {
path: path,
changePath: changePath,
};

return (
<RouterContext.Provider value={contextValue}>
{children}
</RouterContext.Provider>
);
};

export { Router, RouterContext };
37 changes: 37 additions & 0 deletions Caecae/src/Shared/Router/Routes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React, {
useContext,
ReactElement,
ReactNode,
isValidElement,
} from "react";
import { RouterContext } from "./Router";

interface RoutesProps {
children: ReactNode;
}

const Routes: React.FC<RoutesProps> = ({ children }) => {
const { path } = useContext(RouterContext);

let element: ReactElement | null = null;

React.Children.forEach(children, (child) => {
if (!isValidElement(child)) {
return;
}
if (child.type === React.Fragment) {
return;
}
if (!child.props.path || !child.props.element) {
return;
}
if (child.props.path !== path) {
return;
}
element = child.props.element;
});

return element;
};

export default Routes;
File renamed without changes

0 comments on commit fd1adb8

Please sign in to comment.