diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..73debfd --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,68 @@ +name: Deploy to Production Environment + +# This workflow will trigger on any tag/release created on *any* branch +# Make sure to create tags/releases only from the "master" branch for consistency +on: + release: + types: [published] + +jobs: + lint-client: + name: Lint and Export client + runs-on: ubuntu-latest + env: + NEXT_PUBLIC_BASE_PATH: ${{ secrets.NEXT_PUBLIC_BASE_PATH }} + steps: + - name: Checkout the repository + uses: actions/checkout@v3 + - name: Use NodeJS v18.14.2 + uses: actions/setup-node@v3 + with: + node-version: 18.14.2 + - name: Install Dependencies + run: | + cd client + npm install + + - name: Lint Client + run: | + cd client + npm run lint + + - name: Export static files + run: | + cd client + npm run export + mv out/404/index.html out/404.html + + - name: Disable Jekyll + run: | + cd client + touch out/.nojekyll + + - name: Archive Development Artifact + uses: actions/upload-artifact@v3 + with: + name: main-out + path: client/out + retention-days: 3 + + deploy-client: + name: Deploy client to Github Pages + needs: lint-client + runs-on: ubuntu-latest + steps: + - name: Download Artifact + uses: actions/download-artifact@v3 + with: + name: main-out + + - name: List files for publish + run: ls -l -a + + - name: Deploy to Github Pages + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./ + publish_branch: gh-pages diff --git a/README.md b/README.md index 1e9c464..1ee9f7c 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,9 @@ The following dependecies are used for this project. Feel free to experiment usi ### Manual Installation and Usage 1. Navigate to the **/client** directory from the commandline. -2. Run: `npm run install` -3. Run: `npm run dev` +2. Create a `.env` file from the `/client/.env.example` file. Copy it's content when working on localhost. +3. Run: `npm run install` +4. Run: `npm run dev` ### Localhost Development Using Docker diff --git a/client/.env.example b/client/.env.example new file mode 100644 index 0000000..7bf5cd0 --- /dev/null +++ b/client/.env.example @@ -0,0 +1 @@ +NEXT_PUBLIC_BASE_PATH='' \ No newline at end of file diff --git a/client/.eslintrc.json b/client/.eslintrc.json index 5e87096..9bef0c8 100644 --- a/client/.eslintrc.json +++ b/client/.eslintrc.json @@ -2,9 +2,15 @@ "env": { "node": true, "browser": true, - "es6": true + "commonjs": true, + "es2021": true }, - "extends": "next/core-web-vitals", + "extends": [ + "next/core-web-vitals", + "eslint:recommended", + "plugin:react/recommended", + "plugin:react-hooks/recommended" + ], "plugins": [ "eslint-plugin-react", "eslint-plugin-react-hooks" ], @@ -15,7 +21,10 @@ "semi": ["error", "never"], "no-unused-vars": "error", "no-undef": "error", - "no-trailing-spaces": "error", - "no-console": ["error", { "allow": ["error"] }] + // "no-console": ["error", { "allow": ["error"] }], + "react/react-in-jsx-scope": "off", + "no-restricted-imports": [ + "error", { "patterns": ["@/features/*/*"] } + ] } -} +} \ No newline at end of file diff --git a/client/.gitignore b/client/.gitignore index 55175ef..12bc6b5 100644 --- a/client/.gitignore +++ b/client/.gitignore @@ -30,3 +30,5 @@ yarn-error.log* # vercel .vercel + +.env diff --git a/client/README.md b/client/README.md index 63426da..cf1143b 100644 --- a/client/README.md +++ b/client/README.md @@ -25,6 +25,8 @@ The following dependecies are used for this project. Feel free to experiment usi 1. Clone this repository.
`https://github.com/weaponsforge/react-hooks-playground.git` +2. Create a `.env` file from the `/client/.env.example` file. Copy it's content when working on localhost. + ## Available Scripts ### `npm run dev` diff --git a/client/jsconfig.json b/client/jsconfig.json index 10781e7..f93b09f 100644 --- a/client/jsconfig.json +++ b/client/jsconfig.json @@ -3,10 +3,8 @@ "paths": { "@/*": ["./src/*"], "@/hooks/*": ["./src/lib/hooks/*"], - "@/public/*": ["./public/*"], - "@/services/*": ["./src/lib/services/*"], - "@/store/*": ["./src/lib/store/*"], - "@/utils/*": ["./src/lib/utils/*"] + "@/public/*": ["public/*"], + "@/store/*": ["./src/lib/store/*"] } } } diff --git a/client/next.config.js b/client/next.config.js index a843cbe..3bd954f 100644 --- a/client/next.config.js +++ b/client/next.config.js @@ -1,6 +1,15 @@ /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, + trailingSlash: true, + basePath: process.env.NEXT_PUBLIC_BASE_PATH, + assetPrefix: `${process.env.NEXT_PUBLIC_BASE_PATH}/`, + images: { + unoptimized: true + }, + eslint: { + dirs: ['src'] + } } module.exports = nextConfig diff --git a/client/src/common/layout/page/index.js b/client/src/common/layout/page/index.js index 5980e0e..bc0b88c 100644 --- a/client/src/common/layout/page/index.js +++ b/client/src/common/layout/page/index.js @@ -1,6 +1,7 @@ +import PropTypes from 'prop-types' import HeadComponent from '../head' -export default function Page ({ children }) { +function Page ({ children }) { return (
) } + +Page.propTypes = { + children: PropTypes.node +} + +export default Page + diff --git a/client/src/common/ui/card/index.js b/client/src/common/ui/card/index.js index d1d9b1e..e8d260f 100644 --- a/client/src/common/ui/card/index.js +++ b/client/src/common/ui/card/index.js @@ -1,3 +1,4 @@ +import PropTypes from 'prop-types' import styles from './Card.module.css' function Card ({ children }) { @@ -8,4 +9,8 @@ function Card ({ children }) { ) } +Card.propTypes = { + children: PropTypes.node +} + export default Card diff --git a/client/src/components/home/items.json b/client/src/components/home/items.json index 9deab84..a18c185 100644 --- a/client/src/components/home/items.json +++ b/client/src/components/home/items.json @@ -10,5 +10,13 @@ { "name": "useState", "link": "/usestate" + }, + { + "name": "users", + "link": "/users" + }, + { + "name": "useReducer", + "link": "/usereducer" } ] \ No newline at end of file diff --git a/client/src/features/redux/index.js b/client/src/features/redux/index.js new file mode 100644 index 0000000..91c53aa --- /dev/null +++ b/client/src/features/redux/index.js @@ -0,0 +1,7 @@ +import Redux from './reduxcontainer' +import TodoListComponent from './todolist' + +export { + Redux, + TodoListComponent +} diff --git a/client/src/features/redux/reduxcontainer/index.js b/client/src/features/redux/reduxcontainer/index.js new file mode 100644 index 0000000..096bb05 --- /dev/null +++ b/client/src/features/redux/reduxcontainer/index.js @@ -0,0 +1,27 @@ +import { useDispatch } from 'react-redux' +import { todoReceived, todoDelete } from '@/lib/store/todos/todoSlice' + +import ReduxComponent from './redux' + +function Redux () { + const dispatch = useDispatch() + + const addTodo = () => { + dispatch(todoReceived({ + text: 'Hello, world!' + })) + } + + const deleteTodo = (id) => { + dispatch(todoDelete(id)) + } + + return ( + + ) +} + +export default Redux diff --git a/client/src/components/redux/index.js b/client/src/features/redux/reduxcontainer/redux.js similarity index 94% rename from client/src/components/redux/index.js rename to client/src/features/redux/reduxcontainer/redux.js index 0a154f1..b3afc85 100644 --- a/client/src/components/redux/index.js +++ b/client/src/features/redux/reduxcontainer/redux.js @@ -1,7 +1,7 @@ import PropTypes from 'prop-types' import Page from '@/common/layout/page' import Card from '@/common/ui/card' -import TodoListComponent from '@/domain/redux/todolist' +import TodoListComponent from '../todolist' function ReduxComponent ({ addTodo, diff --git a/client/src/domain/redux/todolist/index.js b/client/src/features/redux/todolist/index.js similarity index 100% rename from client/src/domain/redux/todolist/index.js rename to client/src/features/redux/todolist/index.js diff --git a/client/src/features/usereducer/components/contactform/index.js b/client/src/features/usereducer/components/contactform/index.js new file mode 100644 index 0000000..303ca5f --- /dev/null +++ b/client/src/features/usereducer/components/contactform/index.js @@ -0,0 +1,17 @@ +import PropTypes from 'prop-types' + +function ContactForm ({ submitForm }) { + return ( +
+

Add Todo

+
+ +
+ ) +} + +ContactForm.propTypes = { + submitForm: PropTypes.func +} + +export default ContactForm diff --git a/client/src/features/usereducer/components/counter/index.js b/client/src/features/usereducer/components/counter/index.js new file mode 100644 index 0000000..20b6227 --- /dev/null +++ b/client/src/features/usereducer/components/counter/index.js @@ -0,0 +1,18 @@ +import { useState } from 'react' + +function Counter () { + const [count, setCount] = useState(0) + console.log('counter render') + + return ( +
+

Counter

+

{count}

+ +
+ ) +} + +export default Counter diff --git a/client/src/features/usereducer/components/counterlist/index.js b/client/src/features/usereducer/components/counterlist/index.js new file mode 100644 index 0000000..95ab5e6 --- /dev/null +++ b/client/src/features/usereducer/components/counterlist/index.js @@ -0,0 +1,25 @@ +import { useState } from 'react' +import TodoList from '../todolist' + +// Shows unoptimized rerenders from TodoList +function CounterList () { + const [count, setCount] = useState(0) + console.log('counter render') + + return ( +
+

CounterList

+

Features a Todo list using useReducer and a counter increment button/display. The Todo List component is a child of the Counter component.

+ +

Count

+

{count}

+ + + +
+ ) +} + +export default CounterList diff --git a/client/src/features/usereducer/components/counterlistmemo/index.js b/client/src/features/usereducer/components/counterlistmemo/index.js new file mode 100644 index 0000000..08102b8 --- /dev/null +++ b/client/src/features/usereducer/components/counterlistmemo/index.js @@ -0,0 +1,26 @@ +import { useState } from 'react' +import TodoListMemo from '../todolistmemo' + +// Optimize rerenders by wrapping TodoListMemo in a memo() function +// Even it the containing parent's local states changes +function CounterListMemo () { + const [count, setCount] = useState(0) + console.log('counter render') + + return ( +
+

CounterListMemo

+

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().

+ +

Count

+

{count}

+ + + +
+ ) +} + +export default CounterListMemo diff --git a/client/src/features/usereducer/components/counterlistoptimized/index.js b/client/src/features/usereducer/components/counterlistoptimized/index.js new file mode 100644 index 0000000..56e3364 --- /dev/null +++ b/client/src/features/usereducer/components/counterlistoptimized/index.js @@ -0,0 +1,17 @@ +import Counter from '../counter' +import TodoList from '../todolist' + +// Optimized rendering by lowering the states +function CounterListOptimized () { + return ( +
+

CounterListOptimized

+

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.

+ + + +
+ ) +} + +export default CounterListOptimized diff --git a/client/src/features/usereducer/components/todolist/actions.js b/client/src/features/usereducer/components/todolist/actions.js new file mode 100644 index 0000000..4e32d78 --- /dev/null +++ b/client/src/features/usereducer/components/todolist/actions.js @@ -0,0 +1,13 @@ +const ACTION_TYPES = { + CREATE: 'create', + READ: 'read', + UPDATE: 'update', + DELETE: 'delete', + LIST: 'list', + CLEAR_LIST: 'clear_list', + RESET_LIST: 'reset_list' +} + +export { + ACTION_TYPES +} diff --git a/client/src/features/usereducer/components/todolist/index.js b/client/src/features/usereducer/components/todolist/index.js new file mode 100644 index 0000000..64a50f9 --- /dev/null +++ b/client/src/features/usereducer/components/todolist/index.js @@ -0,0 +1,59 @@ +import { useReducer } from 'react' + +import ContactForm from '../contactform' + +import { ACTION_TYPES } from './actions' +import reducer from './reducer' +import data from './todos.json' + +const initialState = { + todos: data +} + +function TodoList () { + const [state, dispatch] = useReducer(reducer, initialState) + + console.log('todolist render') + + const clearItems = () => { + dispatch({ type: ACTION_TYPES.CLEAR_LIST }) + } + + const resetList = () => { + dispatch({ type: ACTION_TYPES.RESET_LIST }) + } + + const removeTodo = (id) => { + dispatch({ type: ACTION_TYPES.DELETE, payload: id }) + } + + const createTodo = (e) => { + e.preventDefault() + const todo = { title: e.target.title.value } + + dispatch({ type: ACTION_TYPES.CREATE, payload: todo }) + } + + return ( +
+
+

Todo List

+   + +


+ + + +
    + {state.todos.map((item, index) => ( +
  • + {item.title}   + +
  • + ))} +
+
+ ) +} + +export default TodoList diff --git a/client/src/features/usereducer/components/todolist/reducer.js b/client/src/features/usereducer/components/todolist/reducer.js new file mode 100644 index 0000000..7c9a496 --- /dev/null +++ b/client/src/features/usereducer/components/todolist/reducer.js @@ -0,0 +1,26 @@ +import { ACTION_TYPES } from './actions' +import data from './todos.json' + +const reducer = (state, action) => { + if (action.type === ACTION_TYPES.CLEAR_LIST) { + return { ...state, todos: [] } + } + + if (action.type === ACTION_TYPES.RESET_LIST) { + return { ...state, todos: data } + } + + if (action.type === ACTION_TYPES.DELETE) { + return { ...state, todos: state.todos.filter(item => item.id !== action.payload) } + } + + if (action.type === ACTION_TYPES.CREATE) { + const id = state.todos.length + const todo = { ...action.payload, id, status: 'pending' } + return { ...state, todos: [...state.todos, todo] } + } + + throw new Error(`Invalid action type ${action.type}.`) +} + +export default reducer diff --git a/client/src/features/usereducer/components/todolist/todos.json b/client/src/features/usereducer/components/todolist/todos.json new file mode 100644 index 0000000..0fe3f21 --- /dev/null +++ b/client/src/features/usereducer/components/todolist/todos.json @@ -0,0 +1,27 @@ +[ + { + "id": 1, + "title": "clean the house", + "status": "pending" + }, + { + "id": 2, + "title": "eat breakfast", + "status": "pending" + }, + { + "id": 3, + "title": "wash clothes", + "status": "pending" + }, + { + "id": 4, + "title": "watch cartoons", + "status": "pending" + }, + { + "id": 5, + "title": "play games", + "status": "pending" + } +] diff --git a/client/src/features/usereducer/components/todolistmemo/index.js b/client/src/features/usereducer/components/todolistmemo/index.js new file mode 100644 index 0000000..6897248 --- /dev/null +++ b/client/src/features/usereducer/components/todolistmemo/index.js @@ -0,0 +1,49 @@ +import { useReducer, memo } from 'react' + +import { ACTION_TYPES } from '../todolist/actions' +import reducer from '../todolist/reducer' +import data from '../todolist/todos.json' + +const initialState = { + todos: data +} + +// A memo() - wrapped version of Todolist +function TodoListMemo () { + const [state, dispatch] = useReducer(reducer, initialState) + + console.log('todolistmemo render') + + const clearItems = () => { + dispatch({ type: ACTION_TYPES.CLEAR_LIST }) + } + + const resetList = () => { + dispatch({ type: ACTION_TYPES.RESET_LIST }) + } + + const removeTodo = (id) => { + dispatch({ type: ACTION_TYPES.DELETE, payload: id }) + } + + return ( +
+
+

Todo List

+   + +


+ +
    + {state.todos.map((item, index) => ( +
  • + {item.title}   + +
  • + ))} +
+
+ ) +} + +export default memo(TodoListMemo) diff --git a/client/src/features/usereducer/index.js b/client/src/features/usereducer/index.js new file mode 100644 index 0000000..d82ac9b --- /dev/null +++ b/client/src/features/usereducer/index.js @@ -0,0 +1,9 @@ +import CounterList from './components/counterlist' +import CounterListOptimized from './components/counterlistoptimized' +import CounterListMemo from './components/counterlistmemo' + +export { + CounterList, + CounterListOptimized, + CounterListMemo +} diff --git a/client/src/features/users/index.js b/client/src/features/users/index.js new file mode 100644 index 0000000..7efd1df --- /dev/null +++ b/client/src/features/users/index.js @@ -0,0 +1,7 @@ +import Users from './userscontainer' +import UserList from './userlist' + +export { + Users, + UserList +} diff --git a/client/src/domain/users/userlist/index.js b/client/src/features/users/userlist/index.js similarity index 82% rename from client/src/domain/users/userlist/index.js rename to client/src/features/users/userlist/index.js index d9d2d07..f1bb8b4 100644 --- a/client/src/domain/users/userlist/index.js +++ b/client/src/features/users/userlist/index.js @@ -1,7 +1,7 @@ import PropTypes from 'prop-types' import { useSelector } from 'react-redux' -function UserListComponent ({ deleteUser }) { +function UserList ({ deleteUser }) { const {ids, entities: users } = useSelector(state => state.users) return ( @@ -20,8 +20,8 @@ function UserListComponent ({ deleteUser }) { ) } -UserListComponent.propTypes = { +UserList.propTypes = { deleteUser: PropTypes.func } -export default UserListComponent +export default UserList diff --git a/client/src/features/users/userscontainer/index.js b/client/src/features/users/userscontainer/index.js new file mode 100644 index 0000000..4585325 --- /dev/null +++ b/client/src/features/users/userscontainer/index.js @@ -0,0 +1,40 @@ +import { useDispatch } from 'react-redux' +import { userReceived, userDelete } from '@/lib/store/users/usersSlice' +import { todoDelete, todoReceived } from '@/lib/store/todos/todoSlice' + +import UsersComponent from './users' + +function Users () { + const dispatch = useDispatch() + + const addUser = () => { + dispatch(userReceived({ + text: 'User anonymous!' + })) + } + + const deleteUser = (id) => { + dispatch(userDelete(id)) + } + + const addTodo = () => { + dispatch(todoReceived({ + text: 'Hello, world!' + })) + } + + const deleteTodo = (id) => { + dispatch(todoDelete(id)) + } + + return ( + + ) +} + +export default Users diff --git a/client/src/components/users/index.js b/client/src/features/users/userscontainer/users.js similarity index 83% rename from client/src/components/users/index.js rename to client/src/features/users/userscontainer/users.js index d947340..4143637 100644 --- a/client/src/components/users/index.js +++ b/client/src/features/users/userscontainer/users.js @@ -1,8 +1,8 @@ import PropTypes from 'prop-types' import Page from '@/common/layout/page' import Card from '@/common/ui/card' -import UserListComponent from '@/domain/users/userlist' -import TodoListComponent from '@/domain/redux/todolist' +import UserList from '../userlist' +import { TodoListComponent } from '@/features/redux' function UsersComponent ({ addUser, @@ -34,7 +34,7 @@ function UsersComponent ({ - + @@ -58,7 +58,9 @@ function UsersComponent ({ UsersComponent.propTypes = { addUser: PropTypes.func, - deleteUser: PropTypes.func + deleteUser: PropTypes.func, + deleteTodo: PropTypes.func, + addTodo: PropTypes.func, } export default UsersComponent diff --git a/client/src/domain/usestate/todolistfull/index.js b/client/src/features/usestate/components/todolistfull/index.js similarity index 100% rename from client/src/domain/usestate/todolistfull/index.js rename to client/src/features/usestate/components/todolistfull/index.js diff --git a/client/src/domain/usestate/todolist/index.js b/client/src/features/usestate/components/todolistv3/index.js similarity index 100% rename from client/src/domain/usestate/todolist/index.js rename to client/src/features/usestate/components/todolistv3/index.js diff --git a/client/src/features/usestate/index.js b/client/src/features/usestate/index.js new file mode 100644 index 0000000..c04061b --- /dev/null +++ b/client/src/features/usestate/index.js @@ -0,0 +1,9 @@ +import UseStateComponent from './usestatecomponent' +import TodoListComponentV3 from './components/todolistv3' +import TodoListComponentFull from './components/todolistfull' + +export { + UseStateComponent, + TodoListComponentFull, + TodoListComponentV3 +} diff --git a/client/src/components/usestate/index.js b/client/src/features/usestate/usestatecomponent/index.js similarity index 93% rename from client/src/components/usestate/index.js rename to client/src/features/usestate/usestatecomponent/index.js index 9b7c353..16f4612 100644 --- a/client/src/components/usestate/index.js +++ b/client/src/features/usestate/usestatecomponent/index.js @@ -2,8 +2,8 @@ import { useState } from 'react' import Card from '@/common/ui/card' import Page from '@/common/layout/page' -import TodoListComponentV3 from '@/domain/usestate/todolist' -import TodoListComponentFull from '@/domain/usestate/todolistfull' +import TodoListComponentV3 from '../components/todolistv3' +import TodoListComponentFull from '../components/todolistfull' function UseStateComponent () { const [state, setState] = useState([]) diff --git a/client/src/domain/usesyncexternalstore/todolist/index.js b/client/src/features/usesyncexternalstore/components/todolistv2/index.js similarity index 100% rename from client/src/domain/usesyncexternalstore/todolist/index.js rename to client/src/features/usesyncexternalstore/components/todolistv2/index.js diff --git a/client/src/features/usesyncexternalstore/index.js b/client/src/features/usesyncexternalstore/index.js new file mode 100644 index 0000000..91e0db9 --- /dev/null +++ b/client/src/features/usesyncexternalstore/index.js @@ -0,0 +1,7 @@ +import SyncExternalStore from './syncexternalstore' +import TodoListComponentV2 from './components/todolistv2' + +export { + SyncExternalStore, + TodoListComponentV2 +} diff --git a/client/src/features/usesyncexternalstore/syncexternalstore/index.js b/client/src/features/usesyncexternalstore/syncexternalstore/index.js new file mode 100644 index 0000000..4f242fb --- /dev/null +++ b/client/src/features/usesyncexternalstore/syncexternalstore/index.js @@ -0,0 +1,15 @@ +import SyncExternalStoreComponent from './syncexternalstore' +import useTodos from '@/lib/hooks/usetodo' + +function SyncExternalStore () { + const { addTodo, deleteTodo } = useTodos() + + return ( + + ) +} + +export default SyncExternalStore diff --git a/client/src/components/usesyncexternalstore/index.js b/client/src/features/usesyncexternalstore/syncexternalstore/syncexternalstore.js similarity index 82% rename from client/src/components/usesyncexternalstore/index.js rename to client/src/features/usesyncexternalstore/syncexternalstore/syncexternalstore.js index 5e7687b..b4ccd39 100644 --- a/client/src/components/usesyncexternalstore/index.js +++ b/client/src/features/usesyncexternalstore/syncexternalstore/syncexternalstore.js @@ -1,9 +1,9 @@ import PropTypes from 'prop-types' import Page from '@/common/layout/page' import Card from '@/common/ui/card' -import TodoListComponentV2 from '@/domain/usesyncexternalstore/todolist' +import TodoListComponentV2 from '../components/todolistv2' -function UseSyncExternalStoreComponent ({ +function SyncExternalStoreComponent ({ addTodo, deleteTodo }) { @@ -49,9 +49,9 @@ function UseSyncExternalStoreComponent ({ ) } -UseSyncExternalStoreComponent.propTypes = { +SyncExternalStoreComponent.propTypes = { addTodo: PropTypes.func, deleteTodo: PropTypes.func } -export default UseSyncExternalStoreComponent +export default SyncExternalStoreComponent diff --git a/client/src/pages/_app.js b/client/src/pages/_app.js index bf9ec3f..22ea4e6 100644 --- a/client/src/pages/_app.js +++ b/client/src/pages/_app.js @@ -1,12 +1,20 @@ +import PropTypes from 'prop-types' import { Provider } from 'react-redux' import { store } from '@/store/store' import '@/styles/globals.css' -export default function App({ Component, pageProps }) { +function App({ Component, pageProps }) { return ( ) } + +App.propTypes = { + Component: PropTypes.func, + pageProps: PropTypes.object +} + +export default App diff --git a/client/src/pages/redux/index.js b/client/src/pages/redux/index.js index d25077b..f40f497 100644 --- a/client/src/pages/redux/index.js +++ b/client/src/pages/redux/index.js @@ -1,27 +1,9 @@ -import { useDispatch } from 'react-redux' -import { todoReceived, todoDelete } from '@/lib/store/todos/todoSlice' - -import ReduxComponent from '@/components/redux' - -function ReduxContainer () { - const dispatch = useDispatch() - - const addTodo = () => { - dispatch(todoReceived({ - text: 'Hello, world!' - })) - } - - const deleteTodo = (id) => { - dispatch(todoDelete(id)) - } +import { Redux } from '@/features/redux' +function ReduxPage () { return ( - + ) } -export default ReduxContainer +export default ReduxPage diff --git a/client/src/pages/usereducer/counterlist.js b/client/src/pages/usereducer/counterlist.js new file mode 100644 index 0000000..448b8a0 --- /dev/null +++ b/client/src/pages/usereducer/counterlist.js @@ -0,0 +1,9 @@ +import { CounterList } from '@/features/usereducer' + +function CounterListPage () { + return ( + + ) +} + +export default CounterListPage diff --git a/client/src/pages/usereducer/counterlistmemo.js b/client/src/pages/usereducer/counterlistmemo.js new file mode 100644 index 0000000..98e81c1 --- /dev/null +++ b/client/src/pages/usereducer/counterlistmemo.js @@ -0,0 +1,9 @@ +import { CounterListMemo } from '@/features/usereducer' + +function CounterListMemoPage () { + return ( + + ) +} + +export default CounterListMemoPage diff --git a/client/src/pages/usereducer/counterlistoptimized.js b/client/src/pages/usereducer/counterlistoptimized.js new file mode 100644 index 0000000..43d8132 --- /dev/null +++ b/client/src/pages/usereducer/counterlistoptimized.js @@ -0,0 +1,9 @@ +import { CounterListOptimized } from '@/features/usereducer' + +function CounterListOptimizedPage () { + return ( + + ) +} + +export default CounterListOptimizedPage diff --git a/client/src/pages/usereducer/index.js b/client/src/pages/usereducer/index.js new file mode 100644 index 0000000..c6110bc --- /dev/null +++ b/client/src/pages/usereducer/index.js @@ -0,0 +1,37 @@ +import Link from 'next/link' + +function ReduxPage () { + return ( +
+

useReducer

+

Checking out useReducer's basic useage and several re-render optimization techniques.

+ +
+ +
    +
  • + +

    CounterList

    +

    Features a Todo list using useReducer and a counter increment button/display. The Todo List component is a child of the Counter component.

    + +
  • + +
  • + +

    CounterListOptimized

    +

    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.

    + +
  • + +
  • + +

    CounterListMemo

    +

    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().

    + +
  • +
+
+ ) +} + +export default ReduxPage diff --git a/client/src/pages/users/index.js b/client/src/pages/users/index.js index d48bff9..4e74b82 100644 --- a/client/src/pages/users/index.js +++ b/client/src/pages/users/index.js @@ -1,40 +1,7 @@ -import { useDispatch } from 'react-redux' -import { userReceived, userDelete } from '@/lib/store/users/usersSlice' -import { todoDelete, todoReceived } from '@/lib/store/todos/todoSlice' +import { Users } from '@/features/users' -import UsersComponent from '@/components/users' - -function Users () { - const dispatch = useDispatch() - - const addUser = () => { - dispatch(userReceived({ - text: 'User anonymous!' - })) - } - - const deleteUser = (id) => { - dispatch(userDelete(id)) - } - - const addTodo = () => { - dispatch(todoReceived({ - text: 'Hello, world!' - })) - } - - const deleteTodo = (id) => { - dispatch(todoDelete(id)) - } - - return ( - - ) +function UsersPage () { + return () } -export default Users +export default UsersPage diff --git a/client/src/pages/usestate/index.js b/client/src/pages/usestate/index.js index c9fa64c..4d1a3c8 100644 --- a/client/src/pages/usestate/index.js +++ b/client/src/pages/usestate/index.js @@ -1,4 +1,4 @@ -import UseStateComponent from '@/components/usestate' +import { UseStateComponent } from '@/features/usestate' function UseStateContainer () { return ( diff --git a/client/src/pages/usesyncexternalstore/index.js b/client/src/pages/usesyncexternalstore/index.js index 078356c..2fd3eb4 100644 --- a/client/src/pages/usesyncexternalstore/index.js +++ b/client/src/pages/usesyncexternalstore/index.js @@ -1,15 +1,7 @@ -import UseSyncExternalStoreComponent from '@/components/usesyncexternalstore' -import useTodos from '@/lib/hooks/usetodo' +import { SyncExternalStore } from '@/features/usesyncexternalstore' function UseSyncExternalStore () { - const { addTodo, deleteTodo } = useTodos() - - return ( - - ) + return () } export default UseSyncExternalStore diff --git a/client/src/styles/globals.css b/client/src/styles/globals.css index 7d2317e..77c99c4 100644 --- a/client/src/styles/globals.css +++ b/client/src/styles/globals.css @@ -79,8 +79,8 @@ html, body { * { box-sizing: border-box; - padding: 0; - margin: 0; + /* padding: 0; + margin: 0; */ } html,