Adds createEntityAdapter

This commit is contained in:
forceoranj
2021-11-13 03:26:22 +01:00
parent 14df6e6399
commit 193f51b54d
32 changed files with 364 additions and 319 deletions

View File

@@ -3,17 +3,20 @@ import { RouteComponentProps } from "react-router-dom"
import { useDispatch, useSelector, shallowEqual } from "react-redux"
import { Helmet } from "react-helmet"
import { AppState, AppThunk } from "../../store"
import { fetchJeuxJavListIfNeed } from "../../store/jeuxJavList"
import { AppState, AppThunk, EntitiesRequest } from "../../store"
import { fetchJeuJavListIfNeed } from "../../store/jeuJavList"
import { fetchEnvieListIfNeed } from "../../store/envieList"
import { JeuxJavList, AddEnvie } from "../../components"
import { JeuJavList, AddEnvie } from "../../components"
import styles from "./styles.module.scss"
export type Props = RouteComponentProps
function useList(stateToProp: (state: AppState) => any, fetchDataIfNeed: () => AppThunk) {
function useList<Entity>(
stateToProp: (state: AppState) => EntitiesRequest<Entity>,
fetchDataIfNeed: () => AppThunk
) {
const dispatch = useDispatch()
const { readyStatus, items } = useSelector(stateToProp, shallowEqual)
const { readyStatus, ids } = useSelector(stateToProp, shallowEqual)
// Fetch client-side data here
useEffect(() => {
@@ -22,12 +25,12 @@ function useList(stateToProp: (state: AppState) => any, fetchDataIfNeed: () => A
}, [dispatch])
return () => {
if (!readyStatus || readyStatus === "invalid" || readyStatus === "request")
if (!readyStatus || readyStatus === "idle" || readyStatus === "request")
return <p>Loading...</p>
if (readyStatus === "failure") return <p>Oops, Failed to load list!</p>
return <JeuxJavList items={items} />
return <JeuJavList ids={ids} />
}
}
@@ -38,7 +41,7 @@ const Home: FC<Props> = (): JSX.Element => {
<Helmet title="Home" />
<AddEnvie dispatch={dispatch} />
{/* {useList((state: AppState) => state.envieList, fetchEnvieListifNeed)()} */}
{useList((state: AppState) => state.jeuxJavList, fetchJeuxJavListIfNeed)()}
{useList((state: AppState) => state.jeuJavList, fetchJeuJavListIfNeed)()}
{/* <button type="button" onClick={() => setList([{id: 3, joueurs: 4, duree: 5, description: "abcd"}])}>
Set list!
</button> */}
@@ -49,7 +52,7 @@ const Home: FC<Props> = (): JSX.Element => {
// Fetch server-side data here
export const loadData = (): AppThunk[] => [
fetchEnvieListIfNeed(),
fetchJeuxJavListIfNeed(),
fetchJeuJavListIfNeed(),
// More pre-fetched actions...
]

View File

@@ -4,13 +4,13 @@
import { render } from "@testing-library/react"
import { MemoryRouter } from "react-router-dom"
import { fetchJeuxJavListIfNeed } from "../../../store/jeuxJavList"
import { fetchJeuJavListIfNeed } from "../../../store/jeuJavList"
import mockStore from "../../../utils/mockStore"
import Home from "../Home"
describe("<Home />", () => {
const renderHelper = (reducer = { readyStatus: "invalid" }) => {
const { dispatch, ProviderWithStore } = mockStore({ jeuxJavList: reducer })
const renderHelper = (reducer = { readyStatus: "idle" }) => {
const { dispatch, ProviderWithStore } = mockStore({ jeuJavList: reducer })
const { container } = render(
<ProviderWithStore>
<MemoryRouter>
@@ -28,7 +28,7 @@ describe("<Home />", () => {
const { dispatch } = renderHelper()
expect(dispatch).toHaveBeenCalledTimes(1)
expect(dispatch.mock.calls[0][0].toString()).toBe(fetchJeuxJavListIfNeed().toString())
expect(dispatch.mock.calls[0][0].toString()).toBe(fetchJeuJavListIfNeed().toString())
})
it("renders the loading status if data invalid", () => {
@@ -50,8 +50,9 @@ describe("<Home />", () => {
it("renders the <List /> if loading was successful", () => {
const reducer = {
readyStatus: "success",
items: [
{
ids: [5],
entities: {
"5": {
id: 5,
titre: "6 qui prend!",
auteur: "Wolfgang Kramer",
@@ -70,7 +71,7 @@ describe("<Home />", () => {
horodatage: "0000-00-00",
ean: "3421272101313",
},
],
},
}
expect(renderHelper(reducer).firstChild).toMatchSnapshot()

View File

@@ -145,7 +145,7 @@ exports[`<Home /> renders the <List /> if loading was successful 1`] = `
</form>
</section>
<div
class="JeuxJavList"
class="JeuJavList"
>
<h4>
Jeux JAV

View File

@@ -4,15 +4,15 @@ import { useDispatch, useSelector, shallowEqual } from "react-redux"
import { Helmet } from "react-helmet"
import { AppState, AppThunk } from "../../store"
import { User } from "../../services/jsonPlaceholder"
import { fetchUserDataIfNeed } from "../../store/userData"
import { Info } from "../../components"
import styles from "./styles.module.scss"
export type Props = RouteComponentProps<{ id: string }>
export type Props = RouteComponentProps<{ memberId: string }>
const UserInfo = ({ match }: Props): JSX.Element => {
const { id } = match.params
const { memberId: rawId } = match.params
const id = +rawId
const dispatch = useDispatch()
const userData = useSelector((state: AppState) => state.userData, shallowEqual)
@@ -21,13 +21,14 @@ const UserInfo = ({ match }: Props): JSX.Element => {
}, [dispatch, id])
const renderInfo = () => {
const userInfo = userData[id]
const userInfo = userData
if (!userInfo || userInfo.readyStatus === "request") return <p>Loading...</p>
if (userInfo.readyStatus === "failure") return <p>Oops! Failed to load data.</p>
if (userInfo.readyStatus === "failure" || !userInfo.entity)
return <p>Oops! Failed to load data.</p>
return <Info item={userInfo.item as User} />
return <Info item={userInfo.entity} />
}
return (
@@ -39,7 +40,7 @@ const UserInfo = ({ match }: Props): JSX.Element => {
}
interface LoadDataArgs {
params: { id: string }
params: { id: number }
}
export const loadData = ({ params }: LoadDataArgs): AppThunk[] => [fetchUserDataIfNeed(params.id)]

View File

@@ -4,19 +4,18 @@
import { render } from "@testing-library/react"
import { MemoryRouter } from "react-router-dom"
import { fetchUserDataIfNeed } from "../../../store/userData"
import mockStore from "../../../utils/mockStore"
import UserInfo from "../UserInfo"
describe("<UserInfo />", () => {
const mockData = {
id: "1",
memberId: 1,
name: "PeL",
phone: "+886 0970...",
email: "forceoranj@gmail.com",
website: "https://www.parisestludique.fr",
}
const { id } = mockData
const { memberId } = mockData
const renderHelper = (reducer = {}) => {
const { dispatch, ProviderWithStore } = mockStore({ userData: reducer })
@@ -25,7 +24,7 @@ describe("<UserInfo />", () => {
<MemoryRouter>
{/*
@ts-expect-error */}
<UserInfo match={{ params: { id } }} />
<UserInfo match={{ params: { memberId } }} />
</MemoryRouter>
</ProviderWithStore>
)
@@ -33,33 +32,24 @@ describe("<UserInfo />", () => {
return { dispatch, firstChild: container.firstChild }
}
it("should fetch data when page loaded", () => {
const { dispatch } = renderHelper()
expect(dispatch).toHaveBeenCalledTimes(1)
expect(dispatch.mock.calls[0][0].toString()).toBe(
fetchUserDataIfNeed(id.toString()).toString()
)
})
it("renders the loading status if data invalid", () => {
expect(renderHelper().firstChild).toMatchSnapshot()
})
it("renders the loading status if requesting data", () => {
const reducer = { [id]: { readyStatus: "request" } }
const reducer = { readyStatus: "request" }
expect(renderHelper(reducer).firstChild).toMatchSnapshot()
})
it("renders an error if loading failed", () => {
const reducer = { [id]: { readyStatus: "failure" } }
const reducer = { readyStatus: "failure" }
expect(renderHelper(reducer).firstChild).toMatchSnapshot()
})
it("renders the <Info /> if loading was successful", () => {
const reducer = { [id]: { readyStatus: "success", item: mockData } }
const reducer = { readyStatus: "success", entity: mockData }
expect(renderHelper(reducer).firstChild).toMatchSnapshot()
})

View File

@@ -47,7 +47,7 @@ exports[`<UserInfo /> renders the loading status if data invalid 1`] = `
class="UserInfo"
>
<p>
Loading...
Oops! Failed to load data.
</p>
</div>
`;