From 5784fd32a2d4a1d00b3513243b2021323ab5b490 Mon Sep 17 00:00:00 2001
From: Mischa Radenovic
Date: Sat, 30 Jan 2021 22:55:31 -0500
Subject: [PATCH] Move logic to redux
Pros:
- [Strongly recomended](https://redux.js.org/style-guide/style-guide#put-as-much-logic-as-possible-in-reducers)
- Easier testing
- Easier mocking of libraries and use of storybook
- More flexible notification handling
---
simplq/src/api/axios-alt.js | 35 +++++++++++++++++++
simplq/src/components/pages/Home/MyQueues.jsx | 18 +++++++++-
simplq/src/store/appSlice.js | 12 +++++++
simplq/src/store/index.js | 10 +++++-
simplq/src/store/queuesSlice.js | 31 ++++++++++++++++
5 files changed, 104 insertions(+), 2 deletions(-)
create mode 100644 simplq/src/api/axios-alt.js
create mode 100644 simplq/src/store/queuesSlice.js
diff --git a/simplq/src/api/axios-alt.js b/simplq/src/api/axios-alt.js
new file mode 100644
index 00000000..b269dbba
--- /dev/null
+++ b/simplq/src/api/axios-alt.js
@@ -0,0 +1,35 @@
+import axios from 'axios';
+// TODO: Read base url from env
+const baseURL = 'https://devbackend.simplq.me/v1';
+
+/**
+ * Async function for for sending request with Auth0
+ *
+ * It combines usRequest and maqeRequest. Since
+ * @auth0/auth0-react can work only wthin a component,
+ * the whole auth object created with useAuth() must be
+ * passed as parameter.
+ *
+ * @param {Object} auth object returned by useAuth() from @auth0/auth0-react.
+ * @param {Object} payload object used in action creators in redux-toolkit.
+ */
+const makeAuthedRequest = async (auth, request) => {
+ // TODO: Is "audience" == "bseURL"? Read from env or use baseURL
+ const accessToken = auth.isAuthenticated
+ ? await auth.getAccessTokenSilently({ audience: 'https://devbackend.simplq.me/v1' })
+ : 'anonymous';
+
+ return axios({
+ baseURL,
+ ...request,
+ headers: {
+ ...request.headers,
+ // Add the Authorization header to the existing headers
+ Authorization: `Bearer ${accessToken}`,
+ },
+ }).then((response) => {
+ return response.data;
+ });
+};
+
+export default makeAuthedRequest;
diff --git a/simplq/src/components/pages/Home/MyQueues.jsx b/simplq/src/components/pages/Home/MyQueues.jsx
index 354fc0ca..953816ca 100644
--- a/simplq/src/components/pages/Home/MyQueues.jsx
+++ b/simplq/src/components/pages/Home/MyQueues.jsx
@@ -3,6 +3,8 @@ import DeleteIcon from '@material-ui/icons/Delete';
import IconButton from '@material-ui/core/IconButton';
import { useHistory } from 'react-router';
import { useAuth0 } from '@auth0/auth0-react';
+import { fetchQueues } from 'store/queuesSlice';
+import { useDispatch, useSelector } from 'react-redux';
import styles from './home.module.scss';
import { QueueRequestFactory } from '../../../api/requestFactory';
import useRequest from '../../../api/useRequest';
@@ -12,12 +14,26 @@ export default () => {
const { requestMaker } = useRequest();
const [myQueues, setMyQueues] = useState([]);
const { isAuthenticated } = useAuth0();
+ const auth = useAuth0();
+ const dispatch = useDispatch();
+ const queues = useSelector((state) => state.queues);
useEffect(() => {
if (isAuthenticated)
requestMaker(QueueRequestFactory.getMyQueues()).then((resp) => setMyQueues(resp.queues));
}, [requestMaker]);
+ useEffect(() => {
+ if (isAuthenticated) {
+ const arg = {
+ auth,
+ // example payload for async thunk
+ payload: { someKey: 'Some value' },
+ };
+ dispatch(fetchQueues(arg));
+ }
+ }, []);
+
if (!isAuthenticated) {
return null;
}
@@ -34,7 +50,7 @@ export default () => {
? "Looks like you don't have any active queues. Start by creating one..."
: 'What would you like to do today? Here are your active queues:'}
- {myQueues.map((queue) => {
+ {queues.map((queue) => {
const handler = () => history.push(`/queue/${queue.queueId}`);
return (
{
+ state.infoText = `Loading queues for ${action.meta.arg.auth.user.name}...`;
+ },
+ [fetchQueues.rejected]: (state, action) => {
+ state.errorText = action.error.message;
+ },
+ [fetchQueues.fulfilled]: (state, action) => {
+ state.infoText = `Number of queues fetched: ${action.payload.queues.length}`;
+ },
+ },
});
export const {
diff --git a/simplq/src/store/index.js b/simplq/src/store/index.js
index 02d6741e..9a032475 100644
--- a/simplq/src/store/index.js
+++ b/simplq/src/store/index.js
@@ -1,10 +1,18 @@
-import { combineReducers, configureStore } from '@reduxjs/toolkit';
+import { combineReducers, configureStore, getDefaultMiddleware } from '@reduxjs/toolkit';
+import queuesSlice from 'store/queuesSlice';
import appReducer from './appSlice';
export const rootReducer = combineReducers({
appReducer,
+ queues: queuesSlice,
});
export const store = configureStore({
reducer: rootReducer,
+ middleware: getDefaultMiddleware({
+ serializableCheck: {
+ // Ignore auth in async thunks
+ ignoredActionPaths: ['meta.arg.auth'],
+ },
+ }),
});
diff --git a/simplq/src/store/queuesSlice.js b/simplq/src/store/queuesSlice.js
new file mode 100644
index 00000000..61a90060
--- /dev/null
+++ b/simplq/src/store/queuesSlice.js
@@ -0,0 +1,31 @@
+/* eslint-disable no-param-reassign */
+
+import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
+import { QueueRequestFactory } from 'api/requestFactory';
+import makeAuthedRequest from 'api/axios-alt';
+
+const fetchQueues = createAsyncThunk('queues/requestStatus', async (arg) => {
+ const { auth } = arg;
+ const authedRequest = makeAuthedRequest(auth, QueueRequestFactory.getMyQueues());
+ const response = await authedRequest;
+ return response;
+});
+
+const queuesSlice = createSlice({
+ name: 'queues',
+ initialState: [],
+ // No reducers for now
+ reducers: {},
+ extraReducers: {
+ // handle fulfiled request
+ [fetchQueues.fulfilled]: (state, action) => {
+ return action.payload.queues;
+ },
+ },
+});
+
+export const { queuesFetched } = queuesSlice.actions;
+
+export { fetchQueues };
+
+export default queuesSlice.reducer;