Created
February 16, 2021 16:59
-
-
Save iax7/6a8a164c6f7025ba929584e5856cee3e to your computer and use it in GitHub Desktop.
Middleware
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { createAction } from "@reduxjs/toolkit"; | |
export const apiCallBegan = createAction("api/callBegan"); | |
export const apiCallSuccess = createAction("api/callSuccess"); | |
export const apiCallFailed = createAction("api/callFailed"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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) | |
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
] | |
}) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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