Skip to content

Commit 372d4ba

Browse files
committed
feat: Explore useReducer, #2
1 parent f4e7342 commit 372d4ba

File tree

17 files changed

+353
-3
lines changed

17 files changed

+353
-3
lines changed

client/.eslintrc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"semi": ["error", "never"],
2222
"no-unused-vars": "error",
2323
"no-undef": "error",
24-
"no-console": ["error", { "allow": ["error"] }],
24+
// "no-console": ["error", { "allow": ["error"] }],
2525
"react/react-in-jsx-scope": "off",
2626
"no-restricted-imports": [
2727
"error", { "patterns": ["@/features/*/*"] }
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import PropTypes from 'prop-types'
2+
3+
function ContactForm ({ submitForm }) {
4+
return (
5+
<form onSubmit={submitForm}>
6+
<h4>Add Todo</h4>
7+
<input type="text" id="title" placeholder="Enter title" /><br />
8+
<button type="submit">Add</button>
9+
</form>
10+
)
11+
}
12+
13+
ContactForm.propTypes = {
14+
submitForm: PropTypes.func
15+
}
16+
17+
export default ContactForm
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { useState } from 'react'
2+
3+
function Counter () {
4+
const [count, setCount] = useState(0)
5+
console.log('counter render')
6+
7+
return (
8+
<div>
9+
<h3>Counter</h3>
10+
<p>{count}</p>
11+
<button onClick={() => setCount(prev => prev += 1)}>
12+
Increment
13+
</button>
14+
</div>
15+
)
16+
}
17+
18+
export default Counter
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { useState } from 'react'
2+
import TodoList from '../todolist'
3+
4+
// Shows unoptimized rerenders from TodoList
5+
function CounterList () {
6+
const [count, setCount] = useState(0)
7+
console.log('counter render')
8+
9+
return (
10+
<div>
11+
<h3>CounterList</h3>
12+
<p>Features a Todo list using useReducer and a counter increment button/display. The Todo List component is a child of the Counter component.</p>
13+
14+
<h3>Count</h3>
15+
<p>{count}</p>
16+
<button onClick={() => setCount(prev => prev += 1)}>
17+
Increment
18+
</button>
19+
20+
<TodoList />
21+
</div>
22+
)
23+
}
24+
25+
export default CounterList
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { useState } from 'react'
2+
import TodoListMemo from '../todolistmemo'
3+
4+
// Optimize rerenders by wrapping TodoListMemo in a memo() function
5+
// Even it the containing parent's local states changes
6+
function CounterListMemo () {
7+
const [count, setCount] = useState(0)
8+
console.log('counter render')
9+
10+
return (
11+
<div>
12+
<h3>CounterListMemo</h3>
13+
<p>Features a Todo list using useReducer and a counter increment button/display. The Todo List component is a child of the Counter component. The TodoList component is wrapped by React.memo().</p>
14+
15+
<h3>Count</h3>
16+
<p>{count}</p>
17+
<button onClick={() => setCount(prev => prev += 1)}>
18+
Increment
19+
</button>
20+
21+
<TodoListMemo />
22+
</div>
23+
)
24+
}
25+
26+
export default CounterListMemo
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import Counter from '../counter'
2+
import TodoList from '../todolist'
3+
4+
// Optimized rendering by lowering the states
5+
function CounterListOptimized () {
6+
return (
7+
<div>
8+
<h3>CounterListOptimized</h3>
9+
<p>Features a Todo list using useReducer and a counter increment button/display. The Todo List component and Counter component are children of a parent component.</p>
10+
11+
<Counter />
12+
<TodoList />
13+
</div>
14+
)
15+
}
16+
17+
export default CounterListOptimized
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
const ACTION_TYPES = {
2+
CREATE: 'create',
3+
READ: 'read',
4+
UPDATE: 'update',
5+
DELETE: 'delete',
6+
LIST: 'list',
7+
CLEAR_LIST: 'clear_list',
8+
RESET_LIST: 'reset_list'
9+
}
10+
11+
export {
12+
ACTION_TYPES
13+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { useReducer } from 'react'
2+
3+
import ContactForm from '../contactform'
4+
5+
import { ACTION_TYPES } from './actions'
6+
import reducer from './reducer'
7+
import data from './todos.json'
8+
9+
const initialState = {
10+
todos: data
11+
}
12+
13+
function TodoList () {
14+
const [state, dispatch] = useReducer(reducer, initialState)
15+
16+
console.log('todolist render')
17+
18+
const clearItems = () => {
19+
dispatch({ type: ACTION_TYPES.CLEAR_LIST })
20+
}
21+
22+
const resetList = () => {
23+
dispatch({ type: ACTION_TYPES.RESET_LIST })
24+
}
25+
26+
const removeTodo = (id) => {
27+
dispatch({ type: ACTION_TYPES.DELETE, payload: id })
28+
}
29+
30+
const createTodo = (e) => {
31+
e.preventDefault()
32+
const todo = { title: e.target.title.value }
33+
34+
dispatch({ type: ACTION_TYPES.CREATE, payload: todo })
35+
}
36+
37+
return (
38+
<div>
39+
<br />
40+
<h3>Todo List</h3>
41+
<button onClick={clearItems}>Clear</button> &nbsp;
42+
<button onClick={resetList}>Reset</button>
43+
<br /><br /><hr />
44+
45+
<ContactForm submitForm={createTodo} />
46+
47+
<ul>
48+
{state.todos.map((item, index) => (
49+
<li key={index}>
50+
<span>{item.title}</span> &nbsp;
51+
<button onClick={() => removeTodo(item.id)}>remove</button>
52+
</li>
53+
))}
54+
</ul>
55+
</div>
56+
)
57+
}
58+
59+
export default TodoList
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { ACTION_TYPES } from './actions'
2+
import data from './todos.json'
3+
4+
const reducer = (state, action) => {
5+
if (action.type === ACTION_TYPES.CLEAR_LIST) {
6+
return { ...state, todos: [] }
7+
}
8+
9+
if (action.type === ACTION_TYPES.RESET_LIST) {
10+
return { ...state, todos: data }
11+
}
12+
13+
if (action.type === ACTION_TYPES.DELETE) {
14+
return { ...state, todos: state.todos.filter(item => item.id !== action.payload) }
15+
}
16+
17+
if (action.type === ACTION_TYPES.CREATE) {
18+
const id = state.todos.length
19+
const todo = { ...action.payload, id, status: 'pending' }
20+
return { ...state, todos: [...state.todos, todo] }
21+
}
22+
23+
throw new Error(`Invalid action type ${action.type}.`)
24+
}
25+
26+
export default reducer
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
[
2+
{
3+
"id": 1,
4+
"title": "clean the house",
5+
"status": "pending"
6+
},
7+
{
8+
"id": 2,
9+
"title": "eat breakfast",
10+
"status": "pending"
11+
},
12+
{
13+
"id": 3,
14+
"title": "wash clothes",
15+
"status": "pending"
16+
},
17+
{
18+
"id": 4,
19+
"title": "watch cartoons",
20+
"status": "pending"
21+
},
22+
{
23+
"id": 5,
24+
"title": "play games",
25+
"status": "pending"
26+
}
27+
]

0 commit comments

Comments
 (0)