Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions demo-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
},
"devDependencies": {
"@babel/core": "^7.16.7",
"@babel/plugin-transform-runtime": "^7.25.9",
"@babel/preset-env": "^7.16.7",
"@babel/preset-react": "^7.16.7",
"@types/express": "^4.17.13",
Expand All @@ -17,6 +18,7 @@
"@types/react-dom": "^17.0.19",
"babel-loader": "^8.2.3",
"copy-webpack-plugin": "^10.2.0",
"core-js": "^3.39.0",
"css-loader": "^6.5.1",
"html-webpack-plugin": "^5.5.0",
"node": "^16.0.0",
Expand Down
86 changes: 73 additions & 13 deletions demo-app/src/client/Components/Buttons.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,82 @@
import React from 'react';
import Increment from './Increment';
import React, { Component, useState } from 'react';

function Buttons(): JSX.Element {
const buttons = [];
for (let i = 0; i < 4; i++) {
buttons.push(<Increment key={i} />);
type ButtonProps = {
id: string;
label: string;
color?: string;
initialCount?: number;
};

type IncrementClassState = {
count: number;
};

class IncrementClass extends Component<ButtonProps, IncrementClassState> {
state = {
count: this.props.initialCount || 0,
};

handleClick = (): void => {
this.setState((prevState: IncrementClassState) => ({
count: prevState.count + 1,
}));
};

render(): JSX.Element {
return (
<div>
<button
id={this.props.id}
className='increment'
onClick={this.handleClick}
style={{ backgroundColor: this.props.color }}
>
{this.props.label} {this.state.count} times.
</button>
</div>
);
}
}

const IncrementFunction = (props: ButtonProps): JSX.Element => {
const [count, setCount] = useState(props.initialCount || 0);

const handleClick = (): void => {
setCount((prev) => prev + 1);
};

return (
<div className='buttons'>
<h1>Stateful Buttons</h1>
<h4>
These buttons are functional components that each manage their own state with the useState
hook.
</h4>
{buttons}
<div>
<button
id={props.id}
className='increment'
onClick={handleClick}
style={{ backgroundColor: props.color }}
>
{props.label} {count} times.
</button>
</div>
);
};

class Buttons extends Component {
render(): JSX.Element {
return (
<div className='buttons'>
<h1>Mixed State Counter</h1>
<h4>First two buttons use class components, last two use function components.</h4>
<IncrementClass id='class1' label='Class Button 1:' color='#f00008' initialCount={5} />
<IncrementClass id='class2' label='Class Button 2:' color='#62d6fb' />
<IncrementFunction
id='func1'
label='Function Button 1:'
color='#6288fb'
initialCount={10}
/>
<IncrementFunction id='func2' label='Function Button 2:' color='#ff6569' />
</div>
);
}
}

export default Buttons;
124 changes: 124 additions & 0 deletions demo-app/src/client/Components/FunctionalReducerCounter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import React, { useState, useReducer } from 'react';

type CounterProps = {
initialCount?: number;
step?: number;
title?: string;
theme?: {
backgroundColor?: string;
textColor?: string;
};
};

type CounterState = {
count: number;
history: number[];
lastAction: string;
};

type CounterAction =
| { type: 'INCREMENT' }
| { type: 'DECREMENT' }
| { type: 'DOUBLE' }
| { type: 'RESET' }
| { type: 'ADD'; payload: number };

function counterReducer(state: CounterState, action: CounterAction, step: number): CounterState {
switch (action.type) {
case 'INCREMENT':
return {
...state,
count: state.count + step,
history: [...state.history, state.count + step],
lastAction: 'INCREMENT',
};
case 'DECREMENT':
return {
...state,
count: state.count - step,
history: [...state.history, state.count - step],
lastAction: 'DECREMENT',
};
case 'DOUBLE':
return {
...state,
count: state.count * 2,
history: [...state.history, state.count * 2],
lastAction: 'DOUBLE',
};
case 'RESET':
return {
count: 0,
history: [],
lastAction: 'RESET',
};
case 'ADD':
return {
...state,
count: state.count + action.payload,
history: [...state.history, state.count + action.payload],
lastAction: `ADD ${action.payload}`,
};
default:
return state;
}
}

function FunctionalReducerCounter({
initialCount = 0,
step = 1,
title = 'Function-based Reducer Counter',
theme = {
backgroundColor: '#ffffff',
textColor: '#330002',
},
}: CounterProps): JSX.Element {
const [clickCount, setClickCount] = useState(0);
const [lastClickTime, setLastClickTime] = useState<Date | null>(null);
const [averageTimeBetweenClicks, setAverageTimeBetweenClicks] = useState<number>(0);
const [state, dispatch] = useReducer(
(state: CounterState, action: CounterAction) => counterReducer(state, action, step),
{
count: initialCount,
history: [],
lastAction: 'none',
},
);

return (
<div
className='reducer-counter'
style={{
backgroundColor: theme.backgroundColor,
color: theme.textColor,
}}
>
<h2>{title}</h2>
<div className='counter-value'>
<h3>Current Count: {state.count}</h3>
</div>

<div className='counter-buttons'>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment (+{step})</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement (-{step})</button>
<button onClick={() => dispatch({ type: 'DOUBLE' })}>Double Value</button>
<button onClick={() => dispatch({ type: 'ADD', payload: 5 })}>Add 5</button>
<button onClick={() => dispatch({ type: 'RESET' })}>Reset</button>
</div>

<div className='counter-info'>
<h4>Last Action: {state.lastAction}</h4>
<h4>History:</h4>
<div className='history-list'>
{state.history.map((value, index) => (
<span key={index}>
{value}
{index < state.history.length - 1 ? ' → ' : ''}
</span>
))}
</div>
</div>
</div>
);
}
export default FunctionalReducerCounter;
98 changes: 98 additions & 0 deletions demo-app/src/client/Components/FunctionalStateCounter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import React, { useState } from 'react';

type CounterProps = {
initialCount?: number;
step?: number;
title?: string;
theme?: {
backgroundColor?: string;
textColor?: string;
};
};

function FunctionalStateCounter({
initialCount = 0,
step = 1,
title = 'Function-based State Counter',
theme = {
backgroundColor: '#ffffff',
textColor: '#330002',
},
}: CounterProps): JSX.Element {
const [count, setCount] = useState(initialCount);
const [history, setHistory] = useState<number[]>([]);
const [lastAction, setLastAction] = useState('none');

const handleAction = (type: string, payload?: number) => {
let newCount = count;
switch (type) {
case 'INCREMENT':
newCount = count + step;
setCount(newCount);
setHistory([...history, newCount]);
setLastAction('INCREMENT');
break;
case 'DECREMENT':
newCount = count - step;
setCount(newCount);
setHistory([...history, newCount]);
setLastAction('DECREMENT');
break;
case 'DOUBLE':
newCount = count * 2;
setCount(newCount);
setHistory([...history, newCount]);
setLastAction('DOUBLE');
break;
case 'ADD':
newCount = count + (payload || 0);
setCount(newCount);
setHistory([...history, newCount]);
setLastAction(`ADD ${payload}`);
break;
case 'RESET':
setCount(0);
setHistory([]);
setLastAction('RESET');
break;
}
};

return (
<div
className='reducer-counter'
style={{
backgroundColor: theme.backgroundColor,
color: theme.textColor,
}}
>
<h2>{title}</h2>
<div className='counter-value'>
<h3>Current Count: {count}</h3>
</div>

<div className='counter-buttons'>
<button onClick={() => handleAction('INCREMENT')}>Increment (+{step})</button>
<button onClick={() => handleAction('DECREMENT')}>Decrement (-{step})</button>
<button onClick={() => handleAction('DOUBLE')}>Double Value</button>
<button onClick={() => handleAction('ADD', 5)}>Add 5</button>
<button onClick={() => handleAction('RESET')}>Reset</button>
</div>

<div className='counter-info'>
<h4>Last Action: {lastAction}</h4>
<h4>History:</h4>
<div className='history-list'>
{history.map((value, index) => (
<span key={index}>
{value}
{index < history.length - 1 ? ' → ' : ''}
</span>
))}
</div>
</div>
</div>
);
}

export default FunctionalStateCounter;
13 changes: 0 additions & 13 deletions demo-app/src/client/Components/Increment.tsx

This file was deleted.

5 changes: 4 additions & 1 deletion demo-app/src/client/Components/Nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ function Nav(): JSX.Element {
Tic-Tac-Toe
</Link>
<Link className='link' to='/buttons'>
Counter
State Counter
</Link>
<Link className='link' to='/reducer'>
Reducer Counter
</Link>
</div>
);
Expand Down
Loading