Skip to content

Commit 9f61e44

Browse files
committed
hook parsing function works for webpack transformed, direct, and namespcae useState patterns
1 parent 29f4f56 commit 9f61e44

File tree

11 files changed

+373
-109
lines changed

11 files changed

+373
-109
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: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,64 @@
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 for IncrementClass state
4+
type IncrementClassState = {
5+
count: number;
6+
};
7+
8+
// Class-based Increment Component
9+
class IncrementClass extends Component<{}, IncrementClassState> {
10+
state = {
11+
count: 0,
12+
};
13+
14+
handleClick = (): void => {
15+
this.setState((prevState: IncrementClassState) => ({
16+
count: prevState.count + 1,
17+
}));
18+
};
19+
20+
render(): JSX.Element {
21+
return (
22+
<div>
23+
<button className='increment' onClick={this.handleClick}>
24+
You clicked me {this.state.count} times.
25+
</button>
26+
</div>
27+
);
828
}
29+
}
30+
31+
// Function-based Increment Component
32+
const IncrementFunction = (): JSX.Element => {
33+
const [count, setCount] = useState(0);
34+
35+
const handleClick = (): void => {
36+
setCount((prev) => prev + 1);
37+
};
938

1039
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}
40+
<div>
41+
<button className='increment' onClick={handleClick}>
42+
You clicked me {count} times.
43+
</button>
1844
</div>
1945
);
46+
};
47+
48+
// Main Buttons Component
49+
class Buttons extends Component {
50+
render(): JSX.Element {
51+
return (
52+
<div className='buttons'>
53+
<h1>Mixed State Counter</h1>
54+
<h4>First two buttons use class components, last two use function components.</h4>
55+
<IncrementClass />
56+
<IncrementClass />
57+
<IncrementFunction />
58+
<IncrementFunction />
59+
</div>
60+
);
61+
}
2062
}
2163

2264
export default Buttons;

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
);
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import React, { Component } from 'react';
2+
3+
type CounterState = {
4+
count: number;
5+
history: number[];
6+
lastAction: string;
7+
};
8+
9+
type CounterAction =
10+
| { type: 'INCREMENT' }
11+
| { type: 'DECREMENT' }
12+
| { type: 'DOUBLE' }
13+
| { type: 'RESET' }
14+
| { type: 'ADD'; payload: number };
15+
16+
// Static reducer function to handle state updates
17+
class ReducerCounter extends Component<{}, CounterState> {
18+
// Initial state definition
19+
static initialState: CounterState = {
20+
count: 0,
21+
history: [],
22+
lastAction: 'none',
23+
};
24+
25+
// Static reducer method to handle state updates
26+
static reducer(state: CounterState, action: CounterAction): CounterState {
27+
switch (action.type) {
28+
case 'INCREMENT':
29+
return {
30+
...state,
31+
count: state.count + 1,
32+
history: [...state.history, state.count + 1],
33+
lastAction: 'INCREMENT',
34+
};
35+
case 'DECREMENT':
36+
return {
37+
...state,
38+
count: state.count - 1,
39+
history: [...state.history, state.count - 1],
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+
...ReducerCounter.initialState,
52+
lastAction: 'RESET',
53+
};
54+
case 'ADD':
55+
return {
56+
...state,
57+
count: state.count + action.payload,
58+
history: [...state.history, state.count + action.payload],
59+
lastAction: `ADD ${action.payload}`,
60+
};
61+
default:
62+
return state;
63+
}
64+
}
65+
66+
constructor(props: {}) {
67+
super(props);
68+
this.state = ReducerCounter.initialState;
69+
70+
// Bind dispatch method
71+
this.dispatch = this.dispatch.bind(this);
72+
}
73+
74+
// Method to handle state updates using the reducer
75+
dispatch(action: CounterAction): void {
76+
this.setState((currentState) => ReducerCounter.reducer(currentState, action));
77+
}
78+
79+
render(): JSX.Element {
80+
return (
81+
<div className='reducer-counter'>
82+
<h2>Class-based Reducer Counter</h2>
83+
<div className='counter-value'>
84+
<h3>Current Count: {this.state.count}</h3>
85+
</div>
86+
87+
<div className='counter-buttons'>
88+
<button onClick={() => this.dispatch({ type: 'INCREMENT' })}>Increment (+1)</button>
89+
<button onClick={() => this.dispatch({ type: 'DECREMENT' })}>Decrement (-1)</button>
90+
<button onClick={() => this.dispatch({ type: 'DOUBLE' })}>Double Value</button>
91+
<button onClick={() => this.dispatch({ type: 'ADD', payload: 5 })}>Add 5</button>
92+
<button onClick={() => this.dispatch({ type: 'RESET' })}>Reset</button>
93+
</div>
94+
95+
<div className='counter-info'>
96+
<h4>Last Action: {this.state.lastAction}</h4>
97+
<h4>History:</h4>
98+
<div className='history-list'>
99+
{this.state.history.map((value, index) => (
100+
<span key={index}>
101+
{value}
102+
{index < this.state.history.length - 1 ? ' → ' : ''}
103+
</span>
104+
))}
105+
</div>
106+
</div>
107+
</div>
108+
);
109+
}
110+
}
111+
112+
export default ReducerCounter;

demo-app/src/client/Router.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Nav from './Components/Nav';
66
import Board from './Components/Board';
77
import Home from './Components/Home';
88
import Buttons from './Components/Buttons';
9+
import ReducerCounter from './Components/ReducerCounter';
910
// import ButtonsWithMoreHooks from './Components/ButtonsWithMoreHooks';
1011

1112
const domNode = document.getElementById('root');
@@ -21,6 +22,7 @@ root.render(
2122
the public facing Buttons page and the fiber node hooks research page "ButtonsWithMoreHooks" */}
2223
<Route path='/buttons' element={<Buttons key='Buttons' />} />
2324
{/* <Route path='/buttons' element={<ButtonsWithMoreHooks key='ButtonsWithMoreHooks'/>} /> */}
25+
<Route path='/reducer' element={<ReducerCounter key='ReducerCounter' />} />
2426
</Routes>
2527
</BrowserRouter>,
2628

0 commit comments

Comments
 (0)