Skip to content

Instantly share code, notes, and snippets.

@iax7
Created February 16, 2021 16:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iax7/6a8a164c6f7025ba929584e5856cee3e to your computer and use it in GitHub Desktop.
Save iax7/6a8a164c6f7025ba929584e5856cee3e to your computer and use it in GitHub Desktop.
Middleware
import { createAction } from "@reduxjs/toolkit";
export const apiCallBegan = createAction("api/callBegan");
export const apiCallSuccess = createAction("api/callSuccess");
export const apiCallFailed = createAction("api/callFailed");
// DUCKS PATTERN - All in one file
import { createSlice } from '@reduxjs/toolkit';
import { createSelector } from 'reselect';
import { apiCallBegan } from "./api";
import moment from 'moment'
const slice = createSlice({
name: "bugs",
initialState: {
list: [],
loading: false,
lastFetch: null
},
reducers: {
// actions: action handlers
bugsRequested: (bugs, action) => {
bugs.loading = true;
},
bugsReceived: (bugs, action) => {
bugs.list = action.payload;
bugs.loading = false;
bugs.lastFetch = Date.now();
},
bugsRequestFailed: (bugs, action) => {
bugs.loading = false;
},
bugAssignedToUser: (bugs, action) => {
const { bugId, userId } = action.payload;
const index = bugs.list.findIndex(bug => bug.id === bugId);
bugs.list[index].userId = userId;
},
// command - event
// addBug - bugAdded
bugAdded: (bugs, action) => {
bugs.list.push(action.payload);
},
// resolveBug (command) - bugResolved (event)
bugResolved: (bugs, action) => {
const index = bugs.list.findIndex(bug => bug.id === action.payload.id)
bugs.list[index].resolved = true;
}
}
});
const { bugAdded, bugResolved, bugAssignedToUser, bugsReceived, bugsRequested, bugsRequestFailed } = slice.actions;
export default slice.reducer;
// Action Creators
const url = "/bugs";
export const loadBugs = () => (dispatch, getState) => {
const { lastFetch } = getState().entities.bugs;
const diffInMinutes = moment().diff(moment(lastFetch), "minutes");
if (diffInMinutes < 10) return;
return dispatch(
apiCallBegan({
url,
onStart: bugsRequested.type,
onSuccess: bugsReceived.type,
onError: bugsRequestFailed.type
})
);
};
export const addBug = bug =>
apiCallBegan({
url,
method: "post",
data: bug,
onSuccess: bugAdded.type
});
export const resolveBug = id =>
apiCallBegan({
// /bugs
// PATCH /bugs/1
url: url + "/" + id,
method: "patch",
data: { resolved: true },
onSuccess: bugResolved.type
});
export const assignBugToUser = (bugId, userId) =>
apiCallBegan({
url: url + "/" + bugId,
method: "patch",
data: { userId },
onSuccess: bugAssignedToUser.type
});
// Selectors
// export const getUnresolvedBugs = state => state.entities.bugs.filter(bug => !bug.resolved);
// With Memoization (cache the result and use it if the state remains unchanged)
export const getUnresolvedBugs = createSelector(
state => state.entities.bugs,
state => state.entities.projects,
(bugs, projects) => bugs.filter(bug => !bug.resolved)
);
export const getBugsByUser = userId =>
createSelector(
state => state.entities.bugs,
bugs => bugs.filter(bug => bug.userId === userId)
);
import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit';
import reducer from './reducer';
import logger from './middleware/logger'
import toast from './middleware/toast';
import api from './middleware/api'
export default function() {
return configureStore({
reducer,
middleware: [
...getDefaultMiddleware(), // Thunk is included here by default
api
]
})
}
import axios from "axios";
import * as actions from "../api";
const api = ({ dispatch }) => next => async action => {
if (action.type !== actions.apiCallBegan.type) return next(action);
const { url, method, data, onStart, onSuccess, onError } = action.payload;
if (onStart) dispatch({ type: onStart });
next(action);
try {
const response = await axios.request({
baseURL: "http://localhost:9001/api",
url,
method,
data
});
// General
dispatch(actions.apiCallSuccess(response.data));
// Specific
if (onSuccess) dispatch({ type: onSuccess, payload: response.data });
} catch (error) {
// General
dispatch(actions.apiCallFailed(error.message));
// Specific
if (onError) dispatch({ type: onError, payload: error.message });
}
};
export default api;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment