From 79195a1892ea5160d390923dddc6ea12b239a335 Mon Sep 17 00:00:00 2001 From: GauthierWebDev Date: Sun, 13 Apr 2025 17:12:10 +0200 Subject: [PATCH] feat: Add Counter component with reducers --- app/data/docs/react/use-reducer/page.md | 238 ++---------------- .../snippets/react/reducer/file-counter.jsx | 22 ++ .../snippets/react/reducer/file-counter.tsx | 22 ++ .../react/reducer/file-counterReducer.js | 30 +++ .../react/reducer/file-counterReducer.ts | 40 +++ .../react/reducer/reducer-action-creator.js | 6 + .../react/reducer/reducer-action-creator.ts | 6 + .../reducer/reducer-actions-constants.js | 4 + .../react/reducer/reducer-actions-enum.js | 6 + .../react/reducer/reducer-actions-enum.ts | 6 + .../reducer/reducer-actions-union-use.ts | 14 ++ .../react/reducer/reducer-actions-union.ts | 6 + .../reducer-dispatch-action-creator.js | 2 + .../reducer/reducer-dispatch-increment.js | 1 + 14 files changed, 183 insertions(+), 220 deletions(-) create mode 100644 app/data/snippets/react/reducer/file-counter.jsx create mode 100644 app/data/snippets/react/reducer/file-counter.tsx create mode 100644 app/data/snippets/react/reducer/file-counterReducer.js create mode 100644 app/data/snippets/react/reducer/file-counterReducer.ts create mode 100644 app/data/snippets/react/reducer/reducer-action-creator.js create mode 100644 app/data/snippets/react/reducer/reducer-action-creator.ts create mode 100644 app/data/snippets/react/reducer/reducer-actions-constants.js create mode 100644 app/data/snippets/react/reducer/reducer-actions-enum.js create mode 100644 app/data/snippets/react/reducer/reducer-actions-enum.ts create mode 100644 app/data/snippets/react/reducer/reducer-actions-union-use.ts create mode 100644 app/data/snippets/react/reducer/reducer-actions-union.ts create mode 100644 app/data/snippets/react/reducer/reducer-dispatch-action-creator.js create mode 100644 app/data/snippets/react/reducer/reducer-dispatch-increment.js diff --git a/app/data/docs/react/use-reducer/page.md b/app/data/docs/react/use-reducer/page.md index 9a46bad..d8440b7 100644 --- a/app/data/docs/react/use-reducer/page.md +++ b/app/data/docs/react/use-reducer/page.md @@ -47,6 +47,7 @@ Comme expliqué plus tôt, un reducer est une fonction qui prend en paramètre u Parlons dans un premier temps de la signature d'un reducer : {% tabs defaultSelectedTab="jsx" %} + {% tab value="jsx" label="JSX" %} {% snippet path="react/reducer/reducer-example.jsx" language="jsx" showLineNumbers=true /%} {% /tab %} @@ -54,6 +55,7 @@ Parlons dans un premier temps de la signature d'un reducer : {% tab value="tsx" label="TSX" %} {% snippet path="react/reducer/reducer-example.tsx" language="tsx" showLineNumbers=true /%} {% /tab %} + {% /tabs %} Comme tu peux le voir, on récupère bien deux paramètres : `state` et `action`. @@ -95,16 +97,13 @@ import { useReducer } from "react"; Ensuite, on va définir notre état initial : {% tabs defaultSelectedTab="js" %} + {% tab value="js" label="JavaScript" %} - {% snippet path="react/reducer/reducer-initial-state.js" language="js" /%} - {% /tab %} {% tab value="ts" label="TypeScript" %} - {% snippet path="react/reducer/reducer-initial-state.ts" language="ts" /%} - {% /tab %} {% /tabs %} @@ -114,15 +113,11 @@ On peut maintenant définir notre reducer : {% tabs defaultSelectedTab="js" %} {% tab value="js" label="JavaScript" %} - {% snippet path="react/reducer/reducer.js" language="js" showLineNumbers=true /%} - {% /tab %} {% tab value="ts" label="TypeScript" %} - {% snippet path="react/reducer/reducer.ts" language="ts" showLineNumbers=true /%} - {% /tab %} {% /tabs %} @@ -143,15 +138,11 @@ Enfin, on peut utiliser le hook useReducer dans notre composant : {% tabs defaultSelectedTab="js" %} {% tab value="js" label="JavaScript" %} - {% snippet path="react/reducer/reducer-hook.js" language="js" /%} - {% /tab %} {% tab value="ts" label="TypeScript" %} - {% snippet path="react/reducer/reducer-hook.ts" language="ts" /%} - {% /tab %} {% /tabs %} @@ -160,9 +151,7 @@ Enfin, on peut utiliser le hook useReducer dans notre composant : Pour modifier l'état, on va donc appeler `dispatch` avec une action : -```js -dispatch({ type: "INCREMENT" }); -``` +{% snippet path="react/reducer/reducer-dispatch-increment.js" language="js" /%} Et voilà, tu sais maintenant comment utiliser `useReducer` dans une application React ! 🎉 @@ -186,12 +175,7 @@ Pour contrer ces problèmes, on va créer des actions et des types d'actions pou Nos types d'actions seront tous des chaînes de caractères. On va donc pouvoir les définir sous forme de constantes. -```js -export const INCREMENT = "INCREMENT"; -export const DECREMENT = "DECREMENT"; -export const RESET = "RESET"; -export const SET = "SET"; -``` +{% snippet path="react/reducer/reducer-actions-constants.js" language="js" /%} {% callout type="note" title="Regrouper les exports" %} @@ -202,29 +186,11 @@ Bien vu ! Et pour TypeScript, on peut aller encore plus loin en créant un `enum {% tabs defaultSelectedTab="js" %} {% tab value="js" label="JavaScript" %} - -```js -export const CounterActionTypes = { - INCREMENT: "INCREMENT", - DECREMENT: "DECREMENT", - RESET: "RESET", - SET: "SET", -}; -``` - +{% snippet path="react/reducer/reducer-actions-enum.js" language="js" /%} {% /tab %} {% tab value="ts" label="TypeScript" %} - -```ts -export const enum CounterActionTypes { - INCREMENT = "INCREMENT", - DECREMENT = "DECREMENT", - RESET = "RESET", - SET = "SET", -} -``` - +{% snippet path="react/reducer/reducer-actions-enum.ts" language="ts" /%} {% /tab %} {% /tabs %} @@ -237,32 +203,11 @@ Si tu utilises JavaScript, je suis désolé de te dire que tu ne peux pas **fort En revanche, si tu utilises TypeScript, tu peux définir les actions de la manière suivante : -```ts -export type CounterAction = - | { type: CounterActionTypes.INCREMENT } - | { type: CounterActionTypes.DECREMENT } - | { type: CounterActionTypes.RESET } - | { type: CounterActionTypes.SET; payload: number }; -``` +{% snippet path="react/reducer/reducer-actions-union.ts" language="ts" /%} Tu pourras alors utiliser `CounterAction` pour typer les actions de ton reducer : -```ts -const reducer = (state: State, action: CounterAction) => { - switch (action.type) { - case CounterActionTypes.INCREMENT: - return { ...state, count: state.count + 1 }; - case CounterActionTypes.DECREMENT: - return { ...state, count: state.count - 1 }; - case CounterActionTypes.RESET: - return { ...state, count: 0 }; - case CounterActionTypes.SET: - return { ...state, count: action.payload }; - default: - return state; - } -}; -``` +{% snippet path="react/reducer/reducer-actions-union-use.ts" language="ts" /%} ### Action creators @@ -271,195 +216,48 @@ Pour éviter de se tromper dans le type de l'action, on peut se créer des fonct {% tabs defaultSelectedTab="js" %} {% tab value="js" label="JavaScript" %} - -```js -export const actions = { - increment: () => ({ type: CounterActionTypes.INCREMENT }), - decrement: () => ({ type: CounterActionTypes.DECREMENT }), - reset: () => ({ type: CounterActionTypes.RESET }), - set: (value) => ({ type: CounterActionTypes.SET, payload: value }), -}; -``` - +{% snippet path="react/reducer/reducer-action-creator.js" language="js" /%} {% /tab %} {% tab value="ts" label="TypeScript" %} - -```ts -export const actions = { - increment: (): CounterAction => ({ type: CounterActionTypes.INCREMENT }), - decrement: (): CounterAction => ({ type: CounterActionTypes.DECREMENT }), - reset: (): CounterAction => ({ type: CounterActionTypes.RESET }), - set: (value: number): CounterAction => ({ type: CounterActionTypes.SET, payload: value }), -}; -``` - +{% snippet path="react/reducer/reducer-action-creator.ts" language="ts" /%} {% /tab %} {% /tabs %} Maintenant le dispatch de nos actions sera beaucoup plus simple et éviter davantage les erreurs lors du développement ! -```js -dispatch(actions.increment()); -dispatch(actions.set(10)); -``` +{% snippet path="react/reducer/reducer-dispatch-action-creator.js" language="js" /%} ## Les fichiers complets On a vu beaucoup de chose et les fichiers sont un peu éparpillés. Pour t'aider à mieux comprendre le fonctionnement du hook `useReducer` et comment l'implementer, voici les fichiers complets : -### Fichier `counterReducer.js` ou `counterReducer.ts` +### Fichier counterReducer.js ou counterReducer.ts {% tabs defaultSelectedTab="js" %} {% tab value="js" label="JavaScript" %} - -```js -const CounterActionTypes = { - INCREMENT: "INCREMENT", - DECREMENT: "DECREMENT", - RESET: "RESET", - SET: "SET", -}; - -export const initialState = { count: 0 }; - -export const reducer = (state, action) => { - switch (action.type) { - case CounterActionTypes.INCREMENT: - return { ...state, count: state.count + 1 }; - case CounterActionTypes.DECREMENT: - return { ...state, count: state.count - 1 }; - case CounterActionTypes.RESET: - return { ...state, count: 0 }; - case CounterActionTypes.SET: - return { ...state, count: action.payload }; - default: - return state; - } -}; - -export const actions = { - increment: () => ({ type: CounterActionTypes.INCREMENT }), - decrement: () => ({ type: CounterActionTypes.DECREMENT }), - reset: () => ({ type: CounterActionTypes.RESET }), - set: (value) => ({ type: CounterActionTypes.SET, payload: value }), -}; -``` - +{% snippet path="react/reducer/file-counterReducer.js" language="js" showLineNumbers=true label="src/reducers/counterReducer.js" /%} {% /tab %} {% tab value="ts" label="TypeScript" %} - -```ts -const enum CounterActionTypes { - INCREMENT = "INCREMENT", - DECREMENT = "DECREMENT", - RESET = "RESET", - SET = "SET", -} - -type State = { - count: number; -}; - -type Action = - | { type: CounterActionTypes.INCREMENT } - | { type: CounterActionTypes.DECREMENT } - | { type: CounterActionTypes.RESET } - | { type: CounterActionTypes.SET; payload: number }; - -export const initialState: State = { count: 0 }; - -export const reducer = (state: State, action: Action) => { - switch (action.type) { - case CounterActionTypes.INCREMENT: - return { ...state, count: state.count + 1 }; - case CounterActionTypes.DECREMENT: - return { ...state, count: state.count - 1 }; - case CounterActionTypes.RESET: - return { ...state, count: 0 }; - case CounterActionTypes.SET: - return { ...state, count: action.payload }; - default: - return state; - } -}; - -export const actions = { - increment: (): Action => ({ type: CounterActionTypes.INCREMENT }), - decrement: (): Action => ({ type: CounterActionTypes.DECREMENT }), - reset: (): Action => ({ type: CounterActionTypes.RESET }), - set: (value: number): Action => ({ type: CounterActionTypes.SET, payload: value }), -}; -``` - +{% snippet path="react/reducer/file-counterReducer.ts" language="ts" showLineNumbers=true label="src/reducers/counterReducer.ts" /%} {% /tab %} {% /tabs %} -### Fichier `Counter.jsx` ou `Counter.tsx` +### Fichier Counter.jsx ou Counter.tsx {% tabs defaultSelectedTab="jsx" %} {% tab value="jsx" label="JSX" %} - -```jsx -import { initialState, actions, reducer } from "../reducers/counterReducer"; -import { useReducer } from "react"; - -const Counter = () => { - const [state, dispatch] = useReducer(reducer, initialState); - - return ( -
-

Count: {state.count}

- - - - - - - - -
- ); -}; - -export default Counter; -``` - +{% snippet path="react/reducer/file-counter.jsx" language="jsx" showLineNumbers=true label="src/components/Counter.jsx" /%} {% /tab %} {% tab value="tsx" label="TSX" %} - -```tsx -import { initialState, actions, reducer } from "../reducers/counterReducer"; -import { useReducer } from "react"; - -const Counter = () => { - const [state, dispatch] = useReducer(reducer, initialState); - - return ( -
-

Count: {state.count}

- - - - - - - - -
- ); -}; - -export default Counter; -``` - +{% snippet path="react/reducer/file-counter.tsx" language="tsx" showLineNumbers=true label="src/components/Counter.tsx" /%} {% /tab %} {% /tabs %} diff --git a/app/data/snippets/react/reducer/file-counter.jsx b/app/data/snippets/react/reducer/file-counter.jsx new file mode 100644 index 0000000..9aced41 --- /dev/null +++ b/app/data/snippets/react/reducer/file-counter.jsx @@ -0,0 +1,22 @@ +import { initialState, actions, reducer } from "../reducers/counterReducer"; +import { useReducer } from "react"; + +const Counter = () => { + const [state, dispatch] = useReducer(reducer, initialState); + + return ( +
+

Count: {state.count}

+ + + + + + + + +
+ ); +}; + +export default Counter; diff --git a/app/data/snippets/react/reducer/file-counter.tsx b/app/data/snippets/react/reducer/file-counter.tsx new file mode 100644 index 0000000..9aced41 --- /dev/null +++ b/app/data/snippets/react/reducer/file-counter.tsx @@ -0,0 +1,22 @@ +import { initialState, actions, reducer } from "../reducers/counterReducer"; +import { useReducer } from "react"; + +const Counter = () => { + const [state, dispatch] = useReducer(reducer, initialState); + + return ( +
+

Count: {state.count}

+ + + + + + + + +
+ ); +}; + +export default Counter; diff --git a/app/data/snippets/react/reducer/file-counterReducer.js b/app/data/snippets/react/reducer/file-counterReducer.js new file mode 100644 index 0000000..99162ad --- /dev/null +++ b/app/data/snippets/react/reducer/file-counterReducer.js @@ -0,0 +1,30 @@ +const CounterActionTypes = { + INCREMENT: "INCREMENT", + DECREMENT: "DECREMENT", + RESET: "RESET", + SET: "SET", +}; + +export const initialState = { count: 0 }; + +export const reducer = (state, action) => { + switch (action.type) { + case CounterActionTypes.INCREMENT: + return { ...state, count: state.count + 1 }; + case CounterActionTypes.DECREMENT: + return { ...state, count: state.count - 1 }; + case CounterActionTypes.RESET: + return { ...state, count: 0 }; + case CounterActionTypes.SET: + return { ...state, count: action.payload }; + default: + return state; + } +}; + +export const actions = { + increment: () => ({ type: CounterActionTypes.INCREMENT }), + decrement: () => ({ type: CounterActionTypes.DECREMENT }), + reset: () => ({ type: CounterActionTypes.RESET }), + set: (value) => ({ type: CounterActionTypes.SET, payload: value }), +}; diff --git a/app/data/snippets/react/reducer/file-counterReducer.ts b/app/data/snippets/react/reducer/file-counterReducer.ts new file mode 100644 index 0000000..caff480 --- /dev/null +++ b/app/data/snippets/react/reducer/file-counterReducer.ts @@ -0,0 +1,40 @@ +const enum CounterActionTypes { + INCREMENT = "INCREMENT", + DECREMENT = "DECREMENT", + RESET = "RESET", + SET = "SET", +} + +type State = { + count: number; +}; + +type Action = + | { type: CounterActionTypes.INCREMENT } + | { type: CounterActionTypes.DECREMENT } + | { type: CounterActionTypes.RESET } + | { type: CounterActionTypes.SET; payload: number }; + +export const initialState: State = { count: 0 }; + +export const reducer = (state: State, action: Action) => { + switch (action.type) { + case CounterActionTypes.INCREMENT: + return { ...state, count: state.count + 1 }; + case CounterActionTypes.DECREMENT: + return { ...state, count: state.count - 1 }; + case CounterActionTypes.RESET: + return { ...state, count: 0 }; + case CounterActionTypes.SET: + return { ...state, count: action.payload }; + default: + return state; + } +}; + +export const actions = { + increment: (): Action => ({ type: CounterActionTypes.INCREMENT }), + decrement: (): Action => ({ type: CounterActionTypes.DECREMENT }), + reset: (): Action => ({ type: CounterActionTypes.RESET }), + set: (value: number): Action => ({ type: CounterActionTypes.SET, payload: value }), +}; diff --git a/app/data/snippets/react/reducer/reducer-action-creator.js b/app/data/snippets/react/reducer/reducer-action-creator.js new file mode 100644 index 0000000..b68b97e --- /dev/null +++ b/app/data/snippets/react/reducer/reducer-action-creator.js @@ -0,0 +1,6 @@ +export const actions = { + increment: () => ({ type: CounterActionTypes.INCREMENT }), + decrement: () => ({ type: CounterActionTypes.DECREMENT }), + reset: () => ({ type: CounterActionTypes.RESET }), + set: (value) => ({ type: CounterActionTypes.SET, payload: value }), +}; diff --git a/app/data/snippets/react/reducer/reducer-action-creator.ts b/app/data/snippets/react/reducer/reducer-action-creator.ts new file mode 100644 index 0000000..c3819a1 --- /dev/null +++ b/app/data/snippets/react/reducer/reducer-action-creator.ts @@ -0,0 +1,6 @@ +export const actions = { + increment: (): CounterAction => ({ type: CounterActionTypes.INCREMENT }), + decrement: (): CounterAction => ({ type: CounterActionTypes.DECREMENT }), + reset: (): CounterAction => ({ type: CounterActionTypes.RESET }), + set: (value: number): CounterAction => ({ type: CounterActionTypes.SET, payload: value }), +}; diff --git a/app/data/snippets/react/reducer/reducer-actions-constants.js b/app/data/snippets/react/reducer/reducer-actions-constants.js new file mode 100644 index 0000000..241e954 --- /dev/null +++ b/app/data/snippets/react/reducer/reducer-actions-constants.js @@ -0,0 +1,4 @@ +export const INCREMENT = "INCREMENT"; +export const DECREMENT = "DECREMENT"; +export const RESET = "RESET"; +export const SET = "SET"; diff --git a/app/data/snippets/react/reducer/reducer-actions-enum.js b/app/data/snippets/react/reducer/reducer-actions-enum.js new file mode 100644 index 0000000..6fa1a1a --- /dev/null +++ b/app/data/snippets/react/reducer/reducer-actions-enum.js @@ -0,0 +1,6 @@ +export const CounterActionTypes = { + INCREMENT: "INCREMENT", + DECREMENT: "DECREMENT", + RESET: "RESET", + SET: "SET", +}; diff --git a/app/data/snippets/react/reducer/reducer-actions-enum.ts b/app/data/snippets/react/reducer/reducer-actions-enum.ts new file mode 100644 index 0000000..5608899 --- /dev/null +++ b/app/data/snippets/react/reducer/reducer-actions-enum.ts @@ -0,0 +1,6 @@ +export const enum CounterActionTypes { + INCREMENT = "INCREMENT", + DECREMENT = "DECREMENT", + RESET = "RESET", + SET = "SET", +} diff --git a/app/data/snippets/react/reducer/reducer-actions-union-use.ts b/app/data/snippets/react/reducer/reducer-actions-union-use.ts new file mode 100644 index 0000000..076fe69 --- /dev/null +++ b/app/data/snippets/react/reducer/reducer-actions-union-use.ts @@ -0,0 +1,14 @@ +const reducer = (state: State, action: CounterAction) => { + switch (action.type) { + case CounterActionTypes.INCREMENT: + return { ...state, count: state.count + 1 }; + case CounterActionTypes.DECREMENT: + return { ...state, count: state.count - 1 }; + case CounterActionTypes.RESET: + return { ...state, count: 0 }; + case CounterActionTypes.SET: + return { ...state, count: action.payload }; + default: + return state; + } +}; diff --git a/app/data/snippets/react/reducer/reducer-actions-union.ts b/app/data/snippets/react/reducer/reducer-actions-union.ts new file mode 100644 index 0000000..5e3df71 --- /dev/null +++ b/app/data/snippets/react/reducer/reducer-actions-union.ts @@ -0,0 +1,6 @@ + +export type CounterAction = + | { type: CounterActionTypes.INCREMENT } + | { type: CounterActionTypes.DECREMENT } + | { type: CounterActionTypes.RESET } + | { type: CounterActionTypes.SET; payload: number }; \ No newline at end of file diff --git a/app/data/snippets/react/reducer/reducer-dispatch-action-creator.js b/app/data/snippets/react/reducer/reducer-dispatch-action-creator.js new file mode 100644 index 0000000..a400480 --- /dev/null +++ b/app/data/snippets/react/reducer/reducer-dispatch-action-creator.js @@ -0,0 +1,2 @@ +dispatch(actions.increment()); +dispatch(actions.set(10)); diff --git a/app/data/snippets/react/reducer/reducer-dispatch-increment.js b/app/data/snippets/react/reducer/reducer-dispatch-increment.js new file mode 100644 index 0000000..a1c3e5c --- /dev/null +++ b/app/data/snippets/react/reducer/reducer-dispatch-increment.js @@ -0,0 +1 @@ +dispatch({ type: "INCREMENT" });