diff --git a/client/src/components/users/index.js b/client/src/components/users/index.js
new file mode 100644
index 0000000..d947340
--- /dev/null
+++ b/client/src/components/users/index.js
@@ -0,0 +1,64 @@
+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'
+
+function UsersComponent ({
+ addUser,
+ deleteUser,
+ deleteTodo,
+ addTodo
+}) {
+ return (
+
+
+ Redux Toolkit - Users
+
+
+ Testing page re-renders and data rendering from multiple redux stores (Users and ToDo) inside regular and deeply-nested components.
+
+
+
+
+ Users
+
+
+ {/** A deeply-nested component */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ToDo
+
+
+
+ )
+}
+
+UsersComponent.propTypes = {
+ addUser: PropTypes.func,
+ deleteUser: PropTypes.func
+}
+
+export default UsersComponent
diff --git a/client/src/domain/users/userlist/index.js b/client/src/domain/users/userlist/index.js
new file mode 100644
index 0000000..d9d2d07
--- /dev/null
+++ b/client/src/domain/users/userlist/index.js
@@ -0,0 +1,27 @@
+import PropTypes from 'prop-types'
+import { useSelector } from 'react-redux'
+
+function UserListComponent ({ deleteUser }) {
+ const {ids, entities: users } = useSelector(state => state.users)
+
+ return (
+
+ {(ids).map(((id, index) => (
+ -
+ id: {users[id].id}, {users[id].text}
+
+
+
+
+ )))}
+
+ )
+}
+
+UserListComponent.propTypes = {
+ deleteUser: PropTypes.func
+}
+
+export default UserListComponent
diff --git a/client/src/lib/store/constants.js b/client/src/lib/store/constants.js
new file mode 100644
index 0000000..5e1e7e4
--- /dev/null
+++ b/client/src/lib/store/constants.js
@@ -0,0 +1,8 @@
+const STATES = {
+ IDLE: 'idle',
+ PENDING: 'pending'
+}
+
+export {
+ STATES
+}
\ No newline at end of file
diff --git a/client/src/lib/store/store.js b/client/src/lib/store/store.js
index 8e86db4..26f1c75 100644
--- a/client/src/lib/store/store.js
+++ b/client/src/lib/store/store.js
@@ -2,10 +2,12 @@ import { combineReducers } from 'redux'
import { configureStore } from '@reduxjs/toolkit'
import todoSlice from '@/lib/store/todos/todoSlice'
+import usersSlice from '@/lib/store/users/usersSlice'
// Reducers
const combinedReducer = combineReducers({
- todos: todoSlice
+ todos: todoSlice,
+ users: usersSlice
})
const rootReducer = (state, action) => {
diff --git a/client/src/lib/store/todos/todoSlice.js b/client/src/lib/store/todos/todoSlice.js
index dc1f51a..a442f57 100644
--- a/client/src/lib/store/todos/todoSlice.js
+++ b/client/src/lib/store/todos/todoSlice.js
@@ -7,10 +7,7 @@ import {
createEntityAdapter
} from '@reduxjs/toolkit'
-const STATES = {
- IDLE: 'idle',
- PENDING: 'pending'
-}
+import { STATES } from '@/lib/store/constants'
// Entiti adapter
const todosAdapter = createEntityAdapter({
diff --git a/client/src/lib/store/users/usersSlice.js b/client/src/lib/store/users/usersSlice.js
new file mode 100644
index 0000000..9e780d0
--- /dev/null
+++ b/client/src/lib/store/users/usersSlice.js
@@ -0,0 +1,51 @@
+// Notes:
+// https://redux.js.org/tutorials/essentials/part-6-performance-normalization#normalized-state-structure
+// https://redux.js.org/tutorials/essentials/part-6-performance-normalization#optimizing-the-posts-list
+
+import {
+ createSlice,
+ createEntityAdapter
+} from '@reduxjs/toolkit'
+
+import { STATES } from '@/lib/store/constants'
+
+// Entiti adapter
+const usersAdapter = createEntityAdapter({
+ selectId: (user) => user.id
+})
+
+// Slice
+const usersSlice = createSlice({
+ name: 'users',
+ initialState: usersAdapter.getInitialState({
+ loading: STATES.IDLE,
+ error: '',
+ success: '',
+ todo: null
+ }),
+ reducers: {
+ userReceived (state, action) {
+ const id = Math.random().toString(36).substring(2, 8)
+
+ state.loading = STATES.IDLE
+ state.todo = { ...action.payload, id }
+ usersAdapter.addOne(state, state.todo)
+
+ },
+ userDelete (state, action) {
+ usersAdapter.removeOne(state, action.payload)
+ },
+ usersReceived (state, action) {
+ state.loading = STATES.IDLE
+ usersAdapter.setAll(state, action.payload)
+ }
+ }
+})
+
+export const {
+ userReceived,
+ usersReceived,
+ userDelete
+} = usersSlice.actions
+
+export default usersSlice.reducer
diff --git a/client/src/pages/users/index.js b/client/src/pages/users/index.js
new file mode 100644
index 0000000..d48bff9
--- /dev/null
+++ b/client/src/pages/users/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 '@/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 (
+
+ )
+}
+
+export default Users