Skip to content

Commit 160b062

Browse files
authored
Merge pull request #2 from oslabs-beta/feature/garrett
Feature/garrett
2 parents 030d2d2 + ff0e102 commit 160b062

File tree

17 files changed

+758
-210
lines changed

17 files changed

+758
-210
lines changed

demo-app/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
},
1010
"devDependencies": {
1111
"@babel/core": "^7.16.7",
12+
"@babel/plugin-transform-runtime": "^7.25.9",
1213
"@babel/preset-env": "^7.16.7",
1314
"@babel/preset-react": "^7.16.7",
1415
"@types/express": "^4.17.13",
@@ -17,6 +18,7 @@
1718
"@types/react-dom": "^17.0.19",
1819
"babel-loader": "^8.2.3",
1920
"copy-webpack-plugin": "^10.2.0",
21+
"core-js": "^3.39.0",
2022
"css-loader": "^6.5.1",
2123
"html-webpack-plugin": "^5.5.0",
2224
"node": "^16.0.0",
Lines changed: 73 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,82 @@
1-
import React from 'react';
2-
import Increment from './Increment';
1+
import React, { Component, useState } from 'react';
32

4-
function Buttons(): JSX.Element {
5-
const buttons = [];
6-
for (let i = 0; i < 4; i++) {
7-
buttons.push(<Increment key={i} />);
3+
type ButtonProps = {
4+
id: string;
5+
label: string;
6+
color?: string;
7+
initialCount?: number;
8+
};
9+
10+
type IncrementClassState = {
11+
count: number;
12+
};
13+
14+
class IncrementClass extends Component<ButtonProps, IncrementClassState> {
15+
state = {
16+
count: this.props.initialCount || 0,
17+
};
18+
19+
handleClick = (): void => {
20+
this.setState((prevState: IncrementClassState) => ({
21+
count: prevState.count + 1,
22+
}));
23+
};
24+
25+
render(): JSX.Element {
26+
return (
27+
<div>
28+
<button
29+
id={this.props.id}
30+
className='increment'
31+
onClick={this.handleClick}
32+
style={{ backgroundColor: this.props.color }}
33+
>
34+
{this.props.label} {this.state.count} times.
35+
</button>
36+
</div>
37+
);
838
}
39+
}
40+
41+
const IncrementFunction = (props: ButtonProps): JSX.Element => {
42+
const [count, setCount] = useState(props.initialCount || 0);
43+
44+
const handleClick = (): void => {
45+
setCount((prev) => prev + 1);
46+
};
947

1048
return (
11-
<div className='buttons'>
12-
<h1>Stateful Buttons</h1>
13-
<h4>
14-
These buttons are functional components that each manage their own state with the useState
15-
hook.
16-
</h4>
17-
{buttons}
49+
<div>
50+
<button
51+
id={props.id}
52+
className='increment'
53+
onClick={handleClick}
54+
style={{ backgroundColor: props.color }}
55+
>
56+
{props.label} {count} times.
57+
</button>
1858
</div>
1959
);
60+
};
61+
62+
class Buttons extends Component {
63+
render(): JSX.Element {
64+
return (
65+
<div className='buttons'>
66+
<h1>Mixed State Counter</h1>
67+
<h4>First two buttons use class components, last two use function components.</h4>
68+
<IncrementClass id='class1' label='Class Button 1:' color='#f00008' initialCount={5} />
69+
<IncrementClass id='class2' label='Class Button 2:' color='#62d6fb' />
70+
<IncrementFunction
71+
id='func1'
72+
label='Function Button 1:'
73+
color='#6288fb'
74+
initialCount={10}
75+
/>
76+
<IncrementFunction id='func2' label='Function Button 2:' color='#ff6569' />
77+
</div>
78+
);
79+
}
2080
}
2181

2282
export default Buttons;
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import React, { useState, useReducer } from 'react';
2+
3+
type CounterProps = {
4+
initialCount?: number;
5+
step?: number;
6+
title?: string;
7+
theme?: {
8+
backgroundColor?: string;
9+
textColor?: string;
10+
};
11+
};
12+
13+
type CounterState = {
14+
count: number;
15+
history: number[];
16+
lastAction: string;
17+
};
18+
19+
type CounterAction =
20+
| { type: 'INCREMENT' }
21+
| { type: 'DECREMENT' }
22+
| { type: 'DOUBLE' }
23+
| { type: 'RESET' }
24+
| { type: 'ADD'; payload: number };
25+
26+
function counterReducer(state: CounterState, action: CounterAction, step: number): CounterState {
27+
switch (action.type) {
28+
case 'INCREMENT':
29+
return {
30+
...state,
31+
count: state.count + step,
32+
history: [...state.history, state.count + step],
33+
lastAction: 'INCREMENT',
34+
};
35+
case 'DECREMENT':
36+
return {
37+
...state,
38+
count: state.count - step,
39+
history: [...state.history, state.count - step],
40+
lastAction: 'DECREMENT',
41+
};
42+
case 'DOUBLE':
43+
return {
44+
...state,
45+
count: state.count * 2,
46+
history: [...state.history, state.count * 2],
47+
lastAction: 'DOUBLE',
48+
};
49+
case 'RESET':
50+
return {
51+
count: 0,
52+
history: [],
53+
lastAction: 'RESET',
54+
};
55+
case 'ADD':
56+
return {
57+
...state,
58+
count: state.count + action.payload,
59+
history: [...state.history, state.count + action.payload],
60+
lastAction: `ADD ${action.payload}`,
61+
};
62+
default:
63+
return state;
64+
}
65+
}
66+
67+
function FunctionalReducerCounter({
68+
initialCount = 0,
69+
step = 1,
70+
title = 'Function-based Reducer Counter',
71+
theme = {
72+
backgroundColor: '#ffffff',
73+
textColor: '#330002',
74+
},
75+
}: CounterProps): JSX.Element {
76+
const [clickCount, setClickCount] = useState(0);
77+
const [lastClickTime, setLastClickTime] = useState<Date | null>(null);
78+
const [averageTimeBetweenClicks, setAverageTimeBetweenClicks] = useState<number>(0);
79+
const [state, dispatch] = useReducer(
80+
(state: CounterState, action: CounterAction) => counterReducer(state, action, step),
81+
{
82+
count: initialCount,
83+
history: [],
84+
lastAction: 'none',
85+
},
86+
);
87+
88+
return (
89+
<div
90+
className='reducer-counter'
91+
style={{
92+
backgroundColor: theme.backgroundColor,
93+
color: theme.textColor,
94+
}}
95+
>
96+
<h2>{title}</h2>
97+
<div className='counter-value'>
98+
<h3>Current Count: {state.count}</h3>
99+
</div>
100+
101+
<div className='counter-buttons'>
102+
<button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment (+{step})</button>
103+
<button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement (-{step})</button>
104+
<button onClick={() => dispatch({ type: 'DOUBLE' })}>Double Value</button>
105+
<button onClick={() => dispatch({ type: 'ADD', payload: 5 })}>Add 5</button>
106+
<button onClick={() => dispatch({ type: 'RESET' })}>Reset</button>
107+
</div>
108+
109+
<div className='counter-info'>
110+
<h4>Last Action: {state.lastAction}</h4>
111+
<h4>History:</h4>
112+
<div className='history-list'>
113+
{state.history.map((value, index) => (
114+
<span key={index}>
115+
{value}
116+
{index < state.history.length - 1 ? ' → ' : ''}
117+
</span>
118+
))}
119+
</div>
120+
</div>
121+
</div>
122+
);
123+
}
124+
export default FunctionalReducerCounter;
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import React, { useState } from 'react';
2+
3+
type CounterProps = {
4+
initialCount?: number;
5+
step?: number;
6+
title?: string;
7+
theme?: {
8+
backgroundColor?: string;
9+
textColor?: string;
10+
};
11+
};
12+
13+
function FunctionalStateCounter({
14+
initialCount = 0,
15+
step = 1,
16+
title = 'Function-based State Counter',
17+
theme = {
18+
backgroundColor: '#ffffff',
19+
textColor: '#330002',
20+
},
21+
}: CounterProps): JSX.Element {
22+
const [count, setCount] = useState(initialCount);
23+
const [history, setHistory] = useState<number[]>([]);
24+
const [lastAction, setLastAction] = useState('none');
25+
26+
const handleAction = (type: string, payload?: number) => {
27+
let newCount = count;
28+
switch (type) {
29+
case 'INCREMENT':
30+
newCount = count + step;
31+
setCount(newCount);
32+
setHistory([...history, newCount]);
33+
setLastAction('INCREMENT');
34+
break;
35+
case 'DECREMENT':
36+
newCount = count - step;
37+
setCount(newCount);
38+
setHistory([...history, newCount]);
39+
setLastAction('DECREMENT');
40+
break;
41+
case 'DOUBLE':
42+
newCount = count * 2;
43+
setCount(newCount);
44+
setHistory([...history, newCount]);
45+
setLastAction('DOUBLE');
46+
break;
47+
case 'ADD':
48+
newCount = count + (payload || 0);
49+
setCount(newCount);
50+
setHistory([...history, newCount]);
51+
setLastAction(`ADD ${payload}`);
52+
break;
53+
case 'RESET':
54+
setCount(0);
55+
setHistory([]);
56+
setLastAction('RESET');
57+
break;
58+
}
59+
};
60+
61+
return (
62+
<div
63+
className='reducer-counter'
64+
style={{
65+
backgroundColor: theme.backgroundColor,
66+
color: theme.textColor,
67+
}}
68+
>
69+
<h2>{title}</h2>
70+
<div className='counter-value'>
71+
<h3>Current Count: {count}</h3>
72+
</div>
73+
74+
<div className='counter-buttons'>
75+
<button onClick={() => handleAction('INCREMENT')}>Increment (+{step})</button>
76+
<button onClick={() => handleAction('DECREMENT')}>Decrement (-{step})</button>
77+
<button onClick={() => handleAction('DOUBLE')}>Double Value</button>
78+
<button onClick={() => handleAction('ADD', 5)}>Add 5</button>
79+
<button onClick={() => handleAction('RESET')}>Reset</button>
80+
</div>
81+
82+
<div className='counter-info'>
83+
<h4>Last Action: {lastAction}</h4>
84+
<h4>History:</h4>
85+
<div className='history-list'>
86+
{history.map((value, index) => (
87+
<span key={index}>
88+
{value}
89+
{index < history.length - 1 ? ' → ' : ''}
90+
</span>
91+
))}
92+
</div>
93+
</div>
94+
</div>
95+
);
96+
}
97+
98+
export default FunctionalStateCounter;

demo-app/src/client/Components/Increment.tsx

Lines changed: 0 additions & 13 deletions
This file was deleted.

demo-app/src/client/Components/Nav.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ function Nav(): JSX.Element {
1111
Tic-Tac-Toe
1212
</Link>
1313
<Link className='link' to='/buttons'>
14-
Counter
14+
State Counter
15+
</Link>
16+
<Link className='link' to='/reducer'>
17+
Reducer Counter
1518
</Link>
1619
</div>
1720
);

0 commit comments

Comments
 (0)