From 5efba58b068b1817803c7e2dc4df2ad312d51b40 Mon Sep 17 00:00:00 2001 From: Arya Emami Date: Mon, 8 Jul 2024 22:48:57 -0500 Subject: [PATCH 1/4] Rename `counterSlice.ts` to `counterSliceAdvanced.ts` --- .../features/counter/{counterSlice.ts => counterSliceAdvanced.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/vite-template-redux/src/features/counter/{counterSlice.ts => counterSliceAdvanced.ts} (100%) diff --git a/packages/vite-template-redux/src/features/counter/counterSlice.ts b/packages/vite-template-redux/src/features/counter/counterSliceAdvanced.ts similarity index 100% rename from packages/vite-template-redux/src/features/counter/counterSlice.ts rename to packages/vite-template-redux/src/features/counter/counterSliceAdvanced.ts From 3828a8dcb03421bde2306fdae68164d55b702316 Mon Sep 17 00:00:00 2001 From: Arya Emami Date: Mon, 8 Jul 2024 23:08:31 -0500 Subject: [PATCH 2/4] Revert some of the 2.0 related changes in the vite template --- .../src/features/counter/Counter.tsx | 1 - .../src/features/counter/counterSlice.test.ts | 4 +- .../src/features/counter/counterSlice.ts | 100 ++++++++++++++++++ .../features/counter/counterSliceAdvanced.ts | 4 + 4 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 packages/vite-template-redux/src/features/counter/counterSlice.ts diff --git a/packages/vite-template-redux/src/features/counter/Counter.tsx b/packages/vite-template-redux/src/features/counter/Counter.tsx index a286d80c..e8bd9549 100644 --- a/packages/vite-template-redux/src/features/counter/Counter.tsx +++ b/packages/vite-template-redux/src/features/counter/Counter.tsx @@ -1,5 +1,4 @@ import { useState } from "react" - import { useAppDispatch, useAppSelector } from "../../app/hooks" import styles from "./Counter.module.css" import { diff --git a/packages/vite-template-redux/src/features/counter/counterSlice.test.ts b/packages/vite-template-redux/src/features/counter/counterSlice.test.ts index 12eafe1f..ea57b483 100644 --- a/packages/vite-template-redux/src/features/counter/counterSlice.test.ts +++ b/packages/vite-template-redux/src/features/counter/counterSlice.test.ts @@ -1,6 +1,6 @@ import type { AppStore } from "../../app/store" import { makeStore } from "../../app/store" -import type { CounterSliceState } from "./counterSlice" +import type { CounterState } from "./counterSlice" import { counterSlice, decrement, @@ -15,7 +15,7 @@ interface LocalTestContext { describe("counter reducer", it => { beforeEach(context => { - const initialState: CounterSliceState = { + const initialState: CounterState = { value: 3, status: "idle", } diff --git a/packages/vite-template-redux/src/features/counter/counterSlice.ts b/packages/vite-template-redux/src/features/counter/counterSlice.ts new file mode 100644 index 00000000..702af72e --- /dev/null +++ b/packages/vite-template-redux/src/features/counter/counterSlice.ts @@ -0,0 +1,100 @@ +// This file demonstrates typical usage of Redux Toolkit's createSlice function +// for defining reducer logic and actions, as well as related thunks and selectors. + +import type { PayloadAction } from "@reduxjs/toolkit" +import { createAsyncThunk, createSlice } from "@reduxjs/toolkit" +import type { AppThunk, RootState } from "../../app/store" +import { fetchCount } from "./counterAPI" + +// Define the TS type for the counter slice's state +export interface CounterState { + value: number + status: "idle" | "loading" | "failed" +} + +// Define the initial value for the slice state +const initialState: CounterState = { + value: 0, + status: "idle", +} + +// Slices contain Redux reducer logic for updating state, and +// generate actions that can be dispatched to trigger those updates. +export const counterSlice = createSlice({ + name: "counter", + initialState, + // The `reducers` field lets us define reducers and generate associated actions + reducers: { + increment: state => { + // Redux Toolkit allows us to write "mutating" logic in reducers. It + // doesn't actually mutate the state because it uses the Immer library, + // which detects changes to a "draft state" and produces a brand new + // immutable state based off those changes + state.value += 1 + }, + decrement: state => { + state.value -= 1 + }, + // Use the PayloadAction type to declare the contents of `action.payload` + incrementByAmount: (state, action: PayloadAction) => { + state.value += action.payload + }, + }, + // The `extraReducers` field lets the slice handle actions defined elsewhere, + // including actions generated by createAsyncThunk or in other slices. + extraReducers: builder => { + builder + // Handle the action types defined by the `incrementAsync` thunk defined below. + // This lets the slice reducer update the state with request status and results. + .addCase(incrementAsync.pending, state => { + state.status = "loading" + }) + .addCase(incrementAsync.fulfilled, (state, action) => { + state.status = "idle" + state.value += action.payload + }) + .addCase(incrementAsync.rejected, state => { + state.status = "failed" + }) + }, +}) + +// Export the generated action creators for use in components +export const { increment, decrement, incrementByAmount } = counterSlice.actions + +// Export the slice reducer for use in the store configuration +export default counterSlice.reducer + +// Selector functions allows us to select a value from the Redux root state. +// Selectors can also be defined inline in the `useSelector` call +// in a component, or inside the `createSlice.selectors` field. +export const selectCount = (state: RootState) => state.counter.value +export const selectStatus = (state: RootState) => state.counter.status + +// The function below is called a thunk, which can contain both sync and async logic +// that has access to both `dispatch` and `getState`. They can be dispatched like +// a regular action: `dispatch(incrementIfOdd(10))`. +// Here's an example of conditionally dispatching actions based on current state. +export const incrementIfOdd = (amount: number): AppThunk => { + return (dispatch, getState) => { + const currentValue = selectCount(getState()) + if (currentValue % 2 === 1) { + dispatch(incrementByAmount(amount)) + } + } +} + +// Thunks are commonly used for async logic like fetching data. +// The `createAsyncThunk` method is used to generate thunks that +// dispatch pending/fulfilled/rejected actions based on a promise. +// In this example, we make a mock async request and return the result. +// The `createSlice.extraReducers` field can handle these actions +// and update the state with the results. +export const incrementAsync = createAsyncThunk( + "counter/fetchCount", + async (amount: number) => { + const response = await fetchCount(amount) + // The value we return becomes the `fulfilled` action payload + return response.data + }, +) diff --git a/packages/vite-template-redux/src/features/counter/counterSliceAdvanced.ts b/packages/vite-template-redux/src/features/counter/counterSliceAdvanced.ts index 07bc1f5c..420944d9 100644 --- a/packages/vite-template-redux/src/features/counter/counterSliceAdvanced.ts +++ b/packages/vite-template-redux/src/features/counter/counterSliceAdvanced.ts @@ -1,3 +1,7 @@ +// This file has the exact same functionality as `counterSlice.ts`, +// but it uses the newest features from Redux Toolkit 2.0. +// These are optional, but may simplify some of your code. + import type { PayloadAction } from "@reduxjs/toolkit" import { createAppSlice } from "../../app/createAppSlice" import type { AppThunk } from "../../app/store" From 68d468b76b51e8672104c10cbbdb3116eff98e4d Mon Sep 17 00:00:00 2001 From: Arya Emami Date: Mon, 8 Jul 2024 23:27:47 -0500 Subject: [PATCH 3/4] Change `counterSlice` files to `counterSliceAdvanced` files --- .../features/counter/{counterSlice.ts => counterSliceAdvanced.ts} | 0 .../features/counter/{counterSlice.js => counterSliceAdvanced.js} | 0 .../features/counter/{counterSlice.ts => counterSliceAdvanced.ts} | 0 .../features/counter/{counterSlice.ts => counterSliceAdvanced.ts} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename packages/cra-template-redux-typescript/template/src/features/counter/{counterSlice.ts => counterSliceAdvanced.ts} (100%) rename packages/cra-template-redux/template/src/features/counter/{counterSlice.js => counterSliceAdvanced.js} (100%) rename packages/expo-template-redux-typescript/src/features/counter/{counterSlice.ts => counterSliceAdvanced.ts} (100%) rename packages/react-native-template-redux-typescript/template/src/features/counter/{counterSlice.ts => counterSliceAdvanced.ts} (100%) diff --git a/packages/cra-template-redux-typescript/template/src/features/counter/counterSlice.ts b/packages/cra-template-redux-typescript/template/src/features/counter/counterSliceAdvanced.ts similarity index 100% rename from packages/cra-template-redux-typescript/template/src/features/counter/counterSlice.ts rename to packages/cra-template-redux-typescript/template/src/features/counter/counterSliceAdvanced.ts diff --git a/packages/cra-template-redux/template/src/features/counter/counterSlice.js b/packages/cra-template-redux/template/src/features/counter/counterSliceAdvanced.js similarity index 100% rename from packages/cra-template-redux/template/src/features/counter/counterSlice.js rename to packages/cra-template-redux/template/src/features/counter/counterSliceAdvanced.js diff --git a/packages/expo-template-redux-typescript/src/features/counter/counterSlice.ts b/packages/expo-template-redux-typescript/src/features/counter/counterSliceAdvanced.ts similarity index 100% rename from packages/expo-template-redux-typescript/src/features/counter/counterSlice.ts rename to packages/expo-template-redux-typescript/src/features/counter/counterSliceAdvanced.ts diff --git a/packages/react-native-template-redux-typescript/template/src/features/counter/counterSlice.ts b/packages/react-native-template-redux-typescript/template/src/features/counter/counterSliceAdvanced.ts similarity index 100% rename from packages/react-native-template-redux-typescript/template/src/features/counter/counterSlice.ts rename to packages/react-native-template-redux-typescript/template/src/features/counter/counterSliceAdvanced.ts From a1853fd5529866c6b2c905bc82c5c30e1e56c5f6 Mon Sep 17 00:00:00 2001 From: Arya Emami Date: Mon, 8 Jul 2024 23:32:38 -0500 Subject: [PATCH 4/4] Revert some of the 2.0 related changes in other templates --- .../src/features/counter/counterSlice.ts | 100 ++++++++++++++++++ .../features/counter/counterSliceAdvanced.ts | 4 + .../src/features/counter/counterSlice.js | 92 ++++++++++++++++ .../features/counter/counterSliceAdvanced.js | 4 + .../src/features/counter/counterSlice.ts | 100 ++++++++++++++++++ .../features/counter/counterSliceAdvanced.ts | 4 + .../src/features/counter/counterSlice.ts | 100 ++++++++++++++++++ .../features/counter/counterSliceAdvanced.ts | 4 + 8 files changed, 408 insertions(+) create mode 100644 packages/cra-template-redux-typescript/template/src/features/counter/counterSlice.ts create mode 100644 packages/cra-template-redux/template/src/features/counter/counterSlice.js create mode 100644 packages/expo-template-redux-typescript/src/features/counter/counterSlice.ts create mode 100644 packages/react-native-template-redux-typescript/template/src/features/counter/counterSlice.ts diff --git a/packages/cra-template-redux-typescript/template/src/features/counter/counterSlice.ts b/packages/cra-template-redux-typescript/template/src/features/counter/counterSlice.ts new file mode 100644 index 00000000..702af72e --- /dev/null +++ b/packages/cra-template-redux-typescript/template/src/features/counter/counterSlice.ts @@ -0,0 +1,100 @@ +// This file demonstrates typical usage of Redux Toolkit's createSlice function +// for defining reducer logic and actions, as well as related thunks and selectors. + +import type { PayloadAction } from "@reduxjs/toolkit" +import { createAsyncThunk, createSlice } from "@reduxjs/toolkit" +import type { AppThunk, RootState } from "../../app/store" +import { fetchCount } from "./counterAPI" + +// Define the TS type for the counter slice's state +export interface CounterState { + value: number + status: "idle" | "loading" | "failed" +} + +// Define the initial value for the slice state +const initialState: CounterState = { + value: 0, + status: "idle", +} + +// Slices contain Redux reducer logic for updating state, and +// generate actions that can be dispatched to trigger those updates. +export const counterSlice = createSlice({ + name: "counter", + initialState, + // The `reducers` field lets us define reducers and generate associated actions + reducers: { + increment: state => { + // Redux Toolkit allows us to write "mutating" logic in reducers. It + // doesn't actually mutate the state because it uses the Immer library, + // which detects changes to a "draft state" and produces a brand new + // immutable state based off those changes + state.value += 1 + }, + decrement: state => { + state.value -= 1 + }, + // Use the PayloadAction type to declare the contents of `action.payload` + incrementByAmount: (state, action: PayloadAction) => { + state.value += action.payload + }, + }, + // The `extraReducers` field lets the slice handle actions defined elsewhere, + // including actions generated by createAsyncThunk or in other slices. + extraReducers: builder => { + builder + // Handle the action types defined by the `incrementAsync` thunk defined below. + // This lets the slice reducer update the state with request status and results. + .addCase(incrementAsync.pending, state => { + state.status = "loading" + }) + .addCase(incrementAsync.fulfilled, (state, action) => { + state.status = "idle" + state.value += action.payload + }) + .addCase(incrementAsync.rejected, state => { + state.status = "failed" + }) + }, +}) + +// Export the generated action creators for use in components +export const { increment, decrement, incrementByAmount } = counterSlice.actions + +// Export the slice reducer for use in the store configuration +export default counterSlice.reducer + +// Selector functions allows us to select a value from the Redux root state. +// Selectors can also be defined inline in the `useSelector` call +// in a component, or inside the `createSlice.selectors` field. +export const selectCount = (state: RootState) => state.counter.value +export const selectStatus = (state: RootState) => state.counter.status + +// The function below is called a thunk, which can contain both sync and async logic +// that has access to both `dispatch` and `getState`. They can be dispatched like +// a regular action: `dispatch(incrementIfOdd(10))`. +// Here's an example of conditionally dispatching actions based on current state. +export const incrementIfOdd = (amount: number): AppThunk => { + return (dispatch, getState) => { + const currentValue = selectCount(getState()) + if (currentValue % 2 === 1) { + dispatch(incrementByAmount(amount)) + } + } +} + +// Thunks are commonly used for async logic like fetching data. +// The `createAsyncThunk` method is used to generate thunks that +// dispatch pending/fulfilled/rejected actions based on a promise. +// In this example, we make a mock async request and return the result. +// The `createSlice.extraReducers` field can handle these actions +// and update the state with the results. +export const incrementAsync = createAsyncThunk( + "counter/fetchCount", + async (amount: number) => { + const response = await fetchCount(amount) + // The value we return becomes the `fulfilled` action payload + return response.data + }, +) diff --git a/packages/cra-template-redux-typescript/template/src/features/counter/counterSliceAdvanced.ts b/packages/cra-template-redux-typescript/template/src/features/counter/counterSliceAdvanced.ts index 07bc1f5c..420944d9 100644 --- a/packages/cra-template-redux-typescript/template/src/features/counter/counterSliceAdvanced.ts +++ b/packages/cra-template-redux-typescript/template/src/features/counter/counterSliceAdvanced.ts @@ -1,3 +1,7 @@ +// This file has the exact same functionality as `counterSlice.ts`, +// but it uses the newest features from Redux Toolkit 2.0. +// These are optional, but may simplify some of your code. + import type { PayloadAction } from "@reduxjs/toolkit" import { createAppSlice } from "../../app/createAppSlice" import type { AppThunk } from "../../app/store" diff --git a/packages/cra-template-redux/template/src/features/counter/counterSlice.js b/packages/cra-template-redux/template/src/features/counter/counterSlice.js new file mode 100644 index 00000000..048980cd --- /dev/null +++ b/packages/cra-template-redux/template/src/features/counter/counterSlice.js @@ -0,0 +1,92 @@ +// This file demonstrates typical usage of Redux Toolkit's createSlice function +// for defining reducer logic and actions, as well as related thunks and selectors. + +import { createAsyncThunk, createSlice } from "@reduxjs/toolkit" +import { fetchCount } from "./counterAPI" + +// Define the initial value for the slice state +const initialState = { + value: 0, + status: "idle", +} + +// Slices contain Redux reducer logic for updating state, and +// generate actions that can be dispatched to trigger those updates. +export const counterSlice = createSlice({ + name: "counter", + initialState, + // The `reducers` field lets us define reducers and generate associated actions + reducers: { + increment: state => { + // Redux Toolkit allows us to write "mutating" logic in reducers. It + // doesn't actually mutate the state because it uses the Immer library, + // which detects changes to a "draft state" and produces a brand new + // immutable state based off those changes + state.value += 1 + }, + decrement: state => { + state.value -= 1 + }, + // Use the PayloadAction type to declare the contents of `action.payload` + incrementByAmount: (state, action) => { + state.value += action.payload + }, + }, + // The `extraReducers` field lets the slice handle actions defined elsewhere, + // including actions generated by createAsyncThunk or in other slices. + extraReducers: builder => { + builder + // Handle the action types defined by the `incrementAsync` thunk defined below. + // This lets the slice reducer update the state with request status and results. + .addCase(incrementAsync.pending, state => { + state.status = "loading" + }) + .addCase(incrementAsync.fulfilled, (state, action) => { + state.status = "idle" + state.value += action.payload + }) + .addCase(incrementAsync.rejected, state => { + state.status = "failed" + }) + }, +}) + +// Export the generated action creators for use in components +export const { increment, decrement, incrementByAmount } = counterSlice.actions + +// Export the slice reducer for use in the store configuration +export default counterSlice.reducer + +// Selector functions allows us to select a value from the Redux root state. +// Selectors can also be defined inline in the `useSelector` call +// in a component, or inside the `createSlice.selectors` field. +export const selectCount = (state) => state.counter.value +export const selectStatus = (state) => state.counter.status + +// The function below is called a thunk, which can contain both sync and async logic +// that has access to both `dispatch` and `getState`. They can be dispatched like +// a regular action: `dispatch(incrementIfOdd(10))`. +// Here's an example of conditionally dispatching actions based on current state. +export const incrementIfOdd = (amount) => { + return (dispatch, getState) => { + const currentValue = selectCount(getState()) + if (currentValue % 2 === 1) { + dispatch(incrementByAmount(amount)) + } + } +} + +// Thunks are commonly used for async logic like fetching data. +// The `createAsyncThunk` method is used to generate thunks that +// dispatch pending/fulfilled/rejected actions based on a promise. +// In this example, we make a mock async request and return the result. +// The `createSlice.extraReducers` field can handle these actions +// and update the state with the results. +export const incrementAsync = createAsyncThunk( + "counter/fetchCount", + async (amount) => { + const response = await fetchCount(amount) + // The value we return becomes the `fulfilled` action payload + return response.data + }, +) diff --git a/packages/cra-template-redux/template/src/features/counter/counterSliceAdvanced.js b/packages/cra-template-redux/template/src/features/counter/counterSliceAdvanced.js index 9c17ab8c..9b432b60 100644 --- a/packages/cra-template-redux/template/src/features/counter/counterSliceAdvanced.js +++ b/packages/cra-template-redux/template/src/features/counter/counterSliceAdvanced.js @@ -1,3 +1,7 @@ +// This file has the exact same functionality as `counterSlice.ts`, +// but it uses the newest features from Redux Toolkit 2.0. +// These are optional, but may simplify some of your code. + import { createAppSlice } from "../../app/createAppSlice" import { fetchCount } from "./counterAPI" diff --git a/packages/expo-template-redux-typescript/src/features/counter/counterSlice.ts b/packages/expo-template-redux-typescript/src/features/counter/counterSlice.ts new file mode 100644 index 00000000..702af72e --- /dev/null +++ b/packages/expo-template-redux-typescript/src/features/counter/counterSlice.ts @@ -0,0 +1,100 @@ +// This file demonstrates typical usage of Redux Toolkit's createSlice function +// for defining reducer logic and actions, as well as related thunks and selectors. + +import type { PayloadAction } from "@reduxjs/toolkit" +import { createAsyncThunk, createSlice } from "@reduxjs/toolkit" +import type { AppThunk, RootState } from "../../app/store" +import { fetchCount } from "./counterAPI" + +// Define the TS type for the counter slice's state +export interface CounterState { + value: number + status: "idle" | "loading" | "failed" +} + +// Define the initial value for the slice state +const initialState: CounterState = { + value: 0, + status: "idle", +} + +// Slices contain Redux reducer logic for updating state, and +// generate actions that can be dispatched to trigger those updates. +export const counterSlice = createSlice({ + name: "counter", + initialState, + // The `reducers` field lets us define reducers and generate associated actions + reducers: { + increment: state => { + // Redux Toolkit allows us to write "mutating" logic in reducers. It + // doesn't actually mutate the state because it uses the Immer library, + // which detects changes to a "draft state" and produces a brand new + // immutable state based off those changes + state.value += 1 + }, + decrement: state => { + state.value -= 1 + }, + // Use the PayloadAction type to declare the contents of `action.payload` + incrementByAmount: (state, action: PayloadAction) => { + state.value += action.payload + }, + }, + // The `extraReducers` field lets the slice handle actions defined elsewhere, + // including actions generated by createAsyncThunk or in other slices. + extraReducers: builder => { + builder + // Handle the action types defined by the `incrementAsync` thunk defined below. + // This lets the slice reducer update the state with request status and results. + .addCase(incrementAsync.pending, state => { + state.status = "loading" + }) + .addCase(incrementAsync.fulfilled, (state, action) => { + state.status = "idle" + state.value += action.payload + }) + .addCase(incrementAsync.rejected, state => { + state.status = "failed" + }) + }, +}) + +// Export the generated action creators for use in components +export const { increment, decrement, incrementByAmount } = counterSlice.actions + +// Export the slice reducer for use in the store configuration +export default counterSlice.reducer + +// Selector functions allows us to select a value from the Redux root state. +// Selectors can also be defined inline in the `useSelector` call +// in a component, or inside the `createSlice.selectors` field. +export const selectCount = (state: RootState) => state.counter.value +export const selectStatus = (state: RootState) => state.counter.status + +// The function below is called a thunk, which can contain both sync and async logic +// that has access to both `dispatch` and `getState`. They can be dispatched like +// a regular action: `dispatch(incrementIfOdd(10))`. +// Here's an example of conditionally dispatching actions based on current state. +export const incrementIfOdd = (amount: number): AppThunk => { + return (dispatch, getState) => { + const currentValue = selectCount(getState()) + if (currentValue % 2 === 1) { + dispatch(incrementByAmount(amount)) + } + } +} + +// Thunks are commonly used for async logic like fetching data. +// The `createAsyncThunk` method is used to generate thunks that +// dispatch pending/fulfilled/rejected actions based on a promise. +// In this example, we make a mock async request and return the result. +// The `createSlice.extraReducers` field can handle these actions +// and update the state with the results. +export const incrementAsync = createAsyncThunk( + "counter/fetchCount", + async (amount: number) => { + const response = await fetchCount(amount) + // The value we return becomes the `fulfilled` action payload + return response.data + }, +) diff --git a/packages/expo-template-redux-typescript/src/features/counter/counterSliceAdvanced.ts b/packages/expo-template-redux-typescript/src/features/counter/counterSliceAdvanced.ts index 07bc1f5c..420944d9 100644 --- a/packages/expo-template-redux-typescript/src/features/counter/counterSliceAdvanced.ts +++ b/packages/expo-template-redux-typescript/src/features/counter/counterSliceAdvanced.ts @@ -1,3 +1,7 @@ +// This file has the exact same functionality as `counterSlice.ts`, +// but it uses the newest features from Redux Toolkit 2.0. +// These are optional, but may simplify some of your code. + import type { PayloadAction } from "@reduxjs/toolkit" import { createAppSlice } from "../../app/createAppSlice" import type { AppThunk } from "../../app/store" diff --git a/packages/react-native-template-redux-typescript/template/src/features/counter/counterSlice.ts b/packages/react-native-template-redux-typescript/template/src/features/counter/counterSlice.ts new file mode 100644 index 00000000..702af72e --- /dev/null +++ b/packages/react-native-template-redux-typescript/template/src/features/counter/counterSlice.ts @@ -0,0 +1,100 @@ +// This file demonstrates typical usage of Redux Toolkit's createSlice function +// for defining reducer logic and actions, as well as related thunks and selectors. + +import type { PayloadAction } from "@reduxjs/toolkit" +import { createAsyncThunk, createSlice } from "@reduxjs/toolkit" +import type { AppThunk, RootState } from "../../app/store" +import { fetchCount } from "./counterAPI" + +// Define the TS type for the counter slice's state +export interface CounterState { + value: number + status: "idle" | "loading" | "failed" +} + +// Define the initial value for the slice state +const initialState: CounterState = { + value: 0, + status: "idle", +} + +// Slices contain Redux reducer logic for updating state, and +// generate actions that can be dispatched to trigger those updates. +export const counterSlice = createSlice({ + name: "counter", + initialState, + // The `reducers` field lets us define reducers and generate associated actions + reducers: { + increment: state => { + // Redux Toolkit allows us to write "mutating" logic in reducers. It + // doesn't actually mutate the state because it uses the Immer library, + // which detects changes to a "draft state" and produces a brand new + // immutable state based off those changes + state.value += 1 + }, + decrement: state => { + state.value -= 1 + }, + // Use the PayloadAction type to declare the contents of `action.payload` + incrementByAmount: (state, action: PayloadAction) => { + state.value += action.payload + }, + }, + // The `extraReducers` field lets the slice handle actions defined elsewhere, + // including actions generated by createAsyncThunk or in other slices. + extraReducers: builder => { + builder + // Handle the action types defined by the `incrementAsync` thunk defined below. + // This lets the slice reducer update the state with request status and results. + .addCase(incrementAsync.pending, state => { + state.status = "loading" + }) + .addCase(incrementAsync.fulfilled, (state, action) => { + state.status = "idle" + state.value += action.payload + }) + .addCase(incrementAsync.rejected, state => { + state.status = "failed" + }) + }, +}) + +// Export the generated action creators for use in components +export const { increment, decrement, incrementByAmount } = counterSlice.actions + +// Export the slice reducer for use in the store configuration +export default counterSlice.reducer + +// Selector functions allows us to select a value from the Redux root state. +// Selectors can also be defined inline in the `useSelector` call +// in a component, or inside the `createSlice.selectors` field. +export const selectCount = (state: RootState) => state.counter.value +export const selectStatus = (state: RootState) => state.counter.status + +// The function below is called a thunk, which can contain both sync and async logic +// that has access to both `dispatch` and `getState`. They can be dispatched like +// a regular action: `dispatch(incrementIfOdd(10))`. +// Here's an example of conditionally dispatching actions based on current state. +export const incrementIfOdd = (amount: number): AppThunk => { + return (dispatch, getState) => { + const currentValue = selectCount(getState()) + if (currentValue % 2 === 1) { + dispatch(incrementByAmount(amount)) + } + } +} + +// Thunks are commonly used for async logic like fetching data. +// The `createAsyncThunk` method is used to generate thunks that +// dispatch pending/fulfilled/rejected actions based on a promise. +// In this example, we make a mock async request and return the result. +// The `createSlice.extraReducers` field can handle these actions +// and update the state with the results. +export const incrementAsync = createAsyncThunk( + "counter/fetchCount", + async (amount: number) => { + const response = await fetchCount(amount) + // The value we return becomes the `fulfilled` action payload + return response.data + }, +) diff --git a/packages/react-native-template-redux-typescript/template/src/features/counter/counterSliceAdvanced.ts b/packages/react-native-template-redux-typescript/template/src/features/counter/counterSliceAdvanced.ts index 07bc1f5c..420944d9 100644 --- a/packages/react-native-template-redux-typescript/template/src/features/counter/counterSliceAdvanced.ts +++ b/packages/react-native-template-redux-typescript/template/src/features/counter/counterSliceAdvanced.ts @@ -1,3 +1,7 @@ +// This file has the exact same functionality as `counterSlice.ts`, +// but it uses the newest features from Redux Toolkit 2.0. +// These are optional, but may simplify some of your code. + import type { PayloadAction } from "@reduxjs/toolkit" import { createAppSlice } from "../../app/createAppSlice" import type { AppThunk } from "../../app/store"