Factors redux tools to access the DB

This commit is contained in:
forceoranj
2021-11-16 03:02:03 +01:00
parent b76fbc78ff
commit bf62510d0a
24 changed files with 506 additions and 462 deletions

View File

@@ -1,13 +1,7 @@
import axios from "axios"
import mockStore from "../../utils/mockStore"
import membre, {
getRequesting,
getSuccess,
getFailure,
fetchMembreData,
initialState,
} from "../membre"
import membre, { getRequesting, getSuccess, getFailure, fetchMembre, initialState } from "../membre"
jest.mock("axios")
@@ -71,7 +65,7 @@ describe("membre action", () => {
// @ts-expect-error
axios.get.mockResolvedValue({ data: mockData })
await dispatch(fetchMembreData(id))
await dispatch(fetchMembre(id))
expect(getActions()).toEqual(expectedActions)
})
@@ -85,7 +79,7 @@ describe("membre action", () => {
// @ts-expect-error
axios.get.mockRejectedValue({ message: mockError })
await dispatch(fetchMembreData(id))
await dispatch(fetchMembre(id))
expect(getActions()).toEqual(expectedActions)
})
})

View File

@@ -1,13 +1,9 @@
import { PayloadAction, createSlice, createEntityAdapter } from "@reduxjs/toolkit"
import { toast } from "react-toastify"
import { StateRequest } from "./utils"
import { Envie, EnvieWithoutId, envieAdd } from "../services/envies"
import { AppThunk } from "."
import { StateRequest, toastError, toastSuccess, elementAddFetch } from "./utils"
import { Envie, envieAdd } from "../services/envies"
const envieAdapter = createEntityAdapter<Envie>({
selectId: (envie) => envie.id,
})
const envieAdapter = createEntityAdapter<Envie>()
const envieAddSlice = createSlice({
name: "addEnvie",
@@ -32,34 +28,11 @@ const envieAddSlice = createSlice({
export default envieAddSlice.reducer
export const { getRequesting, getSuccess, getFailure } = envieAddSlice.actions
export const sendAddEnvie =
(envieWithoutId: EnvieWithoutId): AppThunk =>
async (dispatch) => {
dispatch(getRequesting())
const { error, data } = await envieAdd(envieWithoutId)
if (error) {
dispatch(getFailure(error.message))
toast.error(`Erreur lors de l'ajout d'une envie: ${error.message}`, {
position: "top-center",
autoClose: 6000,
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
})
} else {
dispatch(getSuccess(data as Envie))
toast.success("Envie ajoutée !", {
position: "top-center",
autoClose: 3000,
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
})
}
}
export const fetchEnvieAdd = elementAddFetch(
envieAdd,
getRequesting,
getSuccess,
getFailure,
(error: Error) => toastError(`Erreur lors de l'ajout d'une envie: ${error.message}`),
() => toastSuccess("Envie ajoutée !")
)

View File

@@ -1,13 +1,10 @@
import { PayloadAction, createSlice, createEntityAdapter } from "@reduxjs/toolkit"
import { toast } from "react-toastify"
import { StateRequest } from "./utils"
import { StateRequest, toastError, elementListFetch } from "./utils"
import { Envie, envieListGet } from "../services/envies"
import { AppThunk, AppState } from "."
const envieAdapter = createEntityAdapter<Envie>({
selectId: (envie) => envie.id,
})
const envieAdapter = createEntityAdapter<Envie>()
const envieList = createSlice({
name: "getEnvieList",
@@ -32,26 +29,13 @@ const envieList = createSlice({
export default envieList.reducer
export const { getRequesting, getSuccess, getFailure } = envieList.actions
export const fetchEnvieList = (): AppThunk => async (dispatch) => {
dispatch(getRequesting())
const { error, data } = await envieListGet()
if (error) {
dispatch(getFailure(error.message))
toast.error(`Erreur lors du chargement des envies: ${error.message}`, {
position: "top-center",
autoClose: 6000,
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
})
} else {
dispatch(getSuccess(data as Envie[]))
}
}
export const fetchEnvieList = elementListFetch(
envieListGet,
getRequesting,
getSuccess,
getFailure,
(error: Error) => toastError(`Erreur lors du chargement des envies: ${error.message}`)
)
const shouldFetchEnvieList = (state: AppState) => state.envieList.readyStatus !== "success"

View File

@@ -1,13 +1,10 @@
import { PayloadAction, createSlice, createEntityAdapter } from "@reduxjs/toolkit"
import { toast } from "react-toastify"
import { StateRequest } from "./utils"
import { JeuJav, getJeuJavList } from "../services/jeuJav"
import { StateRequest, toastError, elementListFetch } from "./utils"
import { JeuJav, jeuJavListGet } from "../services/jeuxJav"
import { AppThunk, AppState } from "."
const jeuJavAdapter = createEntityAdapter<JeuJav>({
selectId: (jeuJav) => jeuJav.id,
})
const jeuJavAdapter = createEntityAdapter<JeuJav>()
export const initialState = jeuJavAdapter.getInitialState({
readyStatus: "idle",
@@ -34,26 +31,13 @@ const jeuJavList = createSlice({
export default jeuJavList.reducer
export const { getRequesting, getSuccess, getFailure } = jeuJavList.actions
export const fetchJeuJavList = (): AppThunk => async (dispatch) => {
dispatch(getRequesting())
const { error, data } = await getJeuJavList()
if (error) {
dispatch(getFailure(error.message))
toast.error(`Erreur lors du chargement des jeux JAV: ${error.message}`, {
position: "top-center",
autoClose: 6000,
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
})
} else {
dispatch(getSuccess(data as JeuJav[]))
}
}
export const fetchJeuJavList = elementListFetch(
jeuJavListGet,
getRequesting,
getSuccess,
getFailure,
(error: Error) => toastError(`Erreur lors du chargement des jeux JAV: ${error.message}`)
)
const shouldFetchJeuJavList = (state: AppState) => state.jeuJavList.readyStatus !== "success"

View File

@@ -1,7 +1,6 @@
import { PayloadAction, createSlice } from "@reduxjs/toolkit"
import { toast } from "react-toastify"
import { StateRequest } from "./utils"
import { StateRequest, toastError, elementFetch } from "./utils"
import { Membre, membreGet } from "../services/membres"
import { AppThunk, AppState } from "."
@@ -32,36 +31,21 @@ const membre = createSlice({
export default membre.reducer
export const { getRequesting, getSuccess, getFailure } = membre.actions
export const fetchMembreData =
(id: number): AppThunk =>
async (dispatch) => {
dispatch(getRequesting())
export const fetchMembre = elementFetch(
membreGet,
getRequesting,
getSuccess,
getFailure,
(error: Error) => toastError(`Erreur lors du chargement d'un membre: ${error.message}`)
)
const { error, data } = await membreGet(id)
if (error) {
dispatch(getFailure(error.message))
toast.error(`Erreur lors du chargement du membre ${id}: ${error.message}`, {
position: "top-center",
autoClose: 6000,
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
})
} else {
dispatch(getSuccess(data as Membre))
}
}
const shouldFetchMembreData = (state: AppState, id: number) =>
const shouldFetchMembre = (state: AppState, id: number) =>
state.membre.readyStatus !== "success" || (state.membre.entity && state.membre.entity.id !== id)
export const fetchMembreDataIfNeed =
export const fetchMembreIfNeed =
(id: number): AppThunk =>
(dispatch, getState) => {
if (shouldFetchMembreData(getState(), id)) return dispatch(fetchMembreData(id))
if (shouldFetchMembre(getState(), id)) return dispatch(fetchMembre(id))
return null
}

38
src/store/membreAdd.ts Normal file
View File

@@ -0,0 +1,38 @@
import { PayloadAction, createSlice, createEntityAdapter } from "@reduxjs/toolkit"
import { StateRequest, toastError, toastSuccess, elementAddFetch } from "./utils"
import { Membre, membreAdd } from "../services/membres"
const membreAdapter = createEntityAdapter<Membre>()
const membreAddSlice = createSlice({
name: "addMembre",
initialState: membreAdapter.getInitialState({
readyStatus: "idle",
} as StateRequest),
reducers: {
getRequesting: (state) => {
state.readyStatus = "request"
},
getSuccess: (state, { payload }: PayloadAction<Membre>) => {
state.readyStatus = "success"
membreAdapter.addOne(state, payload)
},
getFailure: (state, { payload }: PayloadAction<string>) => {
state.readyStatus = "failure"
state.error = payload
},
},
})
export default membreAddSlice.reducer
export const { getRequesting, getSuccess, getFailure } = membreAddSlice.actions
export const fetchMembreAdd = elementAddFetch(
membreAdd,
getRequesting,
getSuccess,
getFailure,
(error: Error) => toastError(`Erreur lors de l'ajout d'une membre: ${error.message}`),
() => toastSuccess("Membre ajoutée !")
)

View File

@@ -1,7 +1,6 @@
import { PayloadAction, createSlice, createEntityAdapter } from "@reduxjs/toolkit"
import { toast } from "react-toastify"
import { StateRequest } from "./utils"
import { StateRequest, toastError, elementListFetch } from "./utils"
import { Membre, membreListGet } from "../services/membres"
import { AppThunk, AppState } from "."
@@ -32,26 +31,13 @@ const membreList = createSlice({
export default membreList.reducer
export const { getRequesting, getSuccess, getFailure } = membreList.actions
export const fetchMembreList = (): AppThunk => async (dispatch) => {
dispatch(getRequesting())
const { error, data } = await membreListGet()
if (error) {
dispatch(getFailure(error.message))
toast.error(`Erreur lors du chargement des utilisateurs: ${error.message}`, {
position: "top-center",
autoClose: 6000,
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
})
} else {
dispatch(getSuccess(data as Membre[]))
}
}
export const fetchMembreList = elementListFetch(
membreListGet,
getRequesting,
getSuccess,
getFailure,
(error: Error) => toastError(`Erreur lors du chargement des membres: ${error.message}`)
)
const shouldFetchMembreList = (state: AppState) => state.membreList.readyStatus !== "success"

View File

@@ -1,13 +1,9 @@
import { PayloadAction, createSlice, createEntityAdapter } from "@reduxjs/toolkit"
import { toast } from "react-toastify"
import { StateRequest } from "./utils"
import { StateRequest, toastError, toastSuccess, elementSet } from "./utils"
import { Membre, membreSet } from "../services/membres"
import { AppThunk } from "."
const membreAdapter = createEntityAdapter<Membre>({
selectId: (membre) => membre.id,
})
const membreAdapter = createEntityAdapter<Membre>()
const membreSetSlice = createSlice({
name: "membreSet",
@@ -20,7 +16,7 @@ const membreSetSlice = createSlice({
},
getSuccess: (state, { payload }: PayloadAction<Membre>) => {
state.readyStatus = "success"
membreAdapter.addOne(state, payload)
membreAdapter.setOne(state, payload)
},
getFailure: (state, { payload }: PayloadAction<string>) => {
state.readyStatus = "failure"
@@ -32,34 +28,11 @@ const membreSetSlice = createSlice({
export default membreSetSlice.reducer
export const { getRequesting, getSuccess, getFailure } = membreSetSlice.actions
export const sendMembreSet =
(membre: Membre): AppThunk =>
async (dispatch) => {
dispatch(getRequesting())
const { error, data } = await membreSet(membre)
if (error) {
dispatch(getFailure(error.message))
toast.error(`Erreur lors de la modification d'un membre: ${error.message}`, {
position: "top-center",
autoClose: 6000,
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
})
} else {
dispatch(getSuccess(data as Membre))
toast.success("Membre modifié !", {
position: "top-center",
autoClose: 3000,
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
})
}
}
export const fetchMembreSet = elementSet(
membreSet,
getRequesting,
getSuccess,
getFailure,
(error: Error) => toastError(`Erreur lors de la modification d'un membre: ${error.message}`),
() => toastSuccess("Membre modifié !")
)

View File

@@ -1,18 +1,24 @@
import { History } from "history"
import { connectRouter } from "connected-react-router"
import membreList from "./membreList"
import membre from "./membre"
import jeuJavList from "./jeuJavList"
import envieAdd from "./envieAdd"
import envieList from "./envieList"
import jeuJavList from "./jeuJavList"
import membre from "./membre"
import membreAdd from "./membreAdd"
import membreList from "./membreList"
import membreSet from "./membreSet"
// Use inferred return type for making correctly Redux types
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export default (history: History) => ({
membreList,
membre,
jeuJavList,
envieAdd,
envieList,
jeuJavList,
membre,
membreAdd,
membreList,
membreSet,
router: connectRouter(history) as any,
// Register more reducers...
})

View File

@@ -1,4 +1,156 @@
import { ActionCreatorWithoutPayload, ActionCreatorWithPayload } from "@reduxjs/toolkit"
import { toast } from "react-toastify"
import { AppThunk } from "."
export interface StateRequest {
readyStatus: "idle" | "request" | "success" | "failure"
error?: string
}
export function toastError(message: string): void {
toast.error(message, {
position: "top-center",
autoClose: 6000,
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
})
}
export function toastSuccess(message: string): void {
toast.success(message, {
position: "top-center",
autoClose: 3000,
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
})
}
export function elementFetch<Element>(
elementService: (id: number) => Promise<{
data?: Element | undefined
error?: Error | undefined
}>,
getRequesting: ActionCreatorWithoutPayload<string>,
getSuccess: ActionCreatorWithPayload<Element, string>,
getFailure: ActionCreatorWithPayload<string, string>,
errorMessage: (error: Error) => void = (_error) => {
/* Meant to be empty */
},
successMessage: () => void = () => {
/* Meant to be empty */
}
): (id: number) => AppThunk {
return (id: number): AppThunk =>
async (dispatch) => {
dispatch(getRequesting())
const { error, data } = await elementService(id)
if (error) {
dispatch(getFailure(error.message))
errorMessage(error)
} else {
dispatch(getSuccess(data as Element))
successMessage()
}
}
}
export function elementAddFetch<Element>(
elementAddService: (membreWithoutId: Omit<Element, "id">) => Promise<{
data?: Element | undefined
error?: Error | undefined
}>,
getRequesting: ActionCreatorWithoutPayload<string>,
getSuccess: ActionCreatorWithPayload<Element, string>,
getFailure: ActionCreatorWithPayload<string, string>,
errorMessage: (error: Error) => void = (_error) => {
/* Meant to be empty */
},
successMessage: () => void = () => {
/* Meant to be empty */
}
): (membreWithoutId: Omit<Element, "id">) => AppThunk {
return (membreWithoutId: Omit<Element, "id">): AppThunk =>
async (dispatch) => {
dispatch(getRequesting())
const { error, data } = await elementAddService(membreWithoutId)
if (error) {
dispatch(getFailure(error.message))
errorMessage(error)
} else {
dispatch(getSuccess(data as Element))
successMessage()
}
}
}
export function elementListFetch<Element>(
elementListService: () => Promise<{
data?: Element[] | undefined
error?: Error | undefined
}>,
getRequesting: ActionCreatorWithoutPayload<string>,
getSuccess: ActionCreatorWithPayload<Element[], string>,
getFailure: ActionCreatorWithPayload<string, string>,
errorMessage: (error: Error) => void = (_error) => {
/* Meant to be empty */
},
successMessage: () => void = () => {
/* Meant to be empty */
}
): () => AppThunk {
return (): AppThunk => async (dispatch) => {
dispatch(getRequesting())
const { error, data } = await elementListService()
if (error) {
dispatch(getFailure(error.message))
errorMessage(error)
} else {
dispatch(getSuccess(data as Element[]))
successMessage()
}
}
}
export function elementSet<Element>(
elementSetService: (membre: Element) => Promise<{
data?: Element | undefined
error?: Error | undefined
}>,
getRequesting: ActionCreatorWithoutPayload<string>,
getSuccess: ActionCreatorWithPayload<Element, string>,
getFailure: ActionCreatorWithPayload<string, string>,
errorMessage: (error: Error) => void = (_error) => {
/* Meant to be empty */
},
successMessage: () => void = () => {
/* Meant to be empty */
}
): (element: Element) => AppThunk {
return (element: Element): AppThunk =>
async (dispatch) => {
dispatch(getRequesting())
const { error, data } = await elementSetService(element)
if (error) {
dispatch(getFailure(error.message))
errorMessage(error)
} else {
dispatch(getSuccess(data as Element))
successMessage()
}
}
}