diff --git a/src/features/counter/Counter.module.css b/src/features/counter/Counter.module.css deleted file mode 100644 index 0f4c7c8..0000000 --- a/src/features/counter/Counter.module.css +++ /dev/null @@ -1,79 +0,0 @@ -.row { - display: flex; - align-items: center; - justify-content: center; -} - -.row > button { - margin-left: 4px; - margin-right: 8px; -} - -.row:not(:last-child) { - margin-bottom: 16px; -} - -.value { - font-size: 78px; - padding-left: 16px; - padding-right: 16px; - margin-top: 2px; - font-family: "Courier New", Courier, monospace; -} - -.button { - appearance: none; - font-size: 32px; - padding-left: 12px; - padding-right: 12px; - outline: none; - border: 2px solid transparent; - color: rgb(112, 76, 182); - padding-bottom: 4px; - cursor: pointer; - background: rgba(112, 76, 182, 0.1) none; - border-radius: 2px; - transition: all 0.15s; -} - -.textbox { - font-size: 32px; - padding: 2px; - width: 64px; - text-align: center; - margin-right: 4px; -} - -.button:hover, -.button:focus { - border: 2px solid rgba(112, 76, 182, 0.4); -} - -.button:active { - background-color: rgba(112, 76, 182, 0.2); -} - -.asyncButton { - composes: button; - position: relative; -} - -.asyncButton:after { - content: ""; - background-color: rgba(112, 76, 182, 0.15); - display: block; - position: absolute; - width: 100%; - height: 100%; - left: 0; - top: 0; - opacity: 0; - transition: width 1s linear, - opacity 0.5s ease 1s; -} - -.asyncButton:active:after { - width: 0; - opacity: 1; - transition: 0s; -} diff --git a/src/features/counter/Counter.tsx b/src/features/counter/Counter.tsx deleted file mode 100644 index 6be7255..0000000 --- a/src/features/counter/Counter.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { useState } from "react"; - -import { useAppDispatch, useAppSelector } from "../../hooks/store"; -import styles from "./Counter.module.css"; -import { - decrement, - increment, - incrementAsync, - incrementByAmount, - incrementIfOdd, - selectCount, - selectStatus, -} from "./counterSlice"; - -export const Counter = () => { - const dispatch = useAppDispatch(); - const count = useAppSelector(selectCount); - const status = useAppSelector(selectStatus); - const [incrementAmount, setIncrementAmount] = useState("2"); - - const incrementValue = Number(incrementAmount) || 0; - - return ( -
-
- - - {count} - - -
-
- { - setIncrementAmount(e.target.value); - }} - /> - - - -
-
- ); -}; diff --git a/src/features/counter/counterAPI.ts b/src/features/counter/counterAPI.ts deleted file mode 100644 index c513858..0000000 --- a/src/features/counter/counterAPI.ts +++ /dev/null @@ -1,4 +0,0 @@ -// A mock function to mimic making an async request for data -export const fetchCount = (amount = 1) => { - return new Promise<{ data: number }>((resolve) => setTimeout(() => resolve({ data: amount }), 500)); -}; diff --git a/src/features/counter/counterSlice.ts b/src/features/counter/counterSlice.ts deleted file mode 100644 index 1f3c5b1..0000000 --- a/src/features/counter/counterSlice.ts +++ /dev/null @@ -1,86 +0,0 @@ -import type { PayloadAction } from "@reduxjs/toolkit"; -import { createAppSlice } from "../../app/createAppSlice"; -import type { AppThunk } from "../../app/store"; -import { fetchCount } from "./counterAPI"; - -export interface CounterSliceState { - value: number; - status: "idle" | "loading" | "failed"; -} - -const initialState: CounterSliceState = { - value: 0, - status: "idle", -}; - -// If you are not using async thunks you can use the standalone `createSlice`. -export const counterSlice = createAppSlice({ - name: "counter", - // `createSlice` will infer the state type from the `initialState` argument - initialState, - // The `reducers` field lets us define reducers and generate associated actions - reducers: (create) => ({ - increment: create.reducer((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: create.reducer((state) => { - state.value -= 1; - }), - // Use the `PayloadAction` type to declare the contents of `action.payload` - incrementByAmount: create.reducer((state, action: PayloadAction) => { - state.value += action.payload; - }), - // The function below is called a thunk and allows us to perform async logic. It - // can be dispatched like a regular action: `dispatch(incrementAsync(10))`. This - // will call the thunk with the `dispatch` function as the first argument. Async - // code can then be executed and other actions can be dispatched. Thunks are - // typically used to make async requests. - incrementAsync: create.asyncThunk( - async (amount: number) => { - const response = await fetchCount(amount); - // The value we return becomes the `fulfilled` action payload - return response.data; - }, - { - pending: (state) => { - state.status = "loading"; - }, - fulfilled: (state, action) => { - state.status = "idle"; - state.value += action.payload; - }, - rejected: (state) => { - state.status = "failed"; - }, - }, - ), - }), - // You can define your selectors here. These selectors receive the slice - // state as their first argument. - selectors: { - selectCount: (counter) => counter.value, - selectStatus: (counter) => counter.status, - }, -}); - -// Action creators are generated for each case reducer function. -export const { decrement, increment, incrementByAmount, incrementAsync } = counterSlice.actions; - -// Selectors returned by `slice.selectors` take the root state as their first argument. -export const { selectCount, selectStatus } = counterSlice.selectors; - -// We can also write thunks by hand, which may contain both sync and async logic. -// Here's an example of conditionally dispatching actions based on current state. -export const incrementIfOdd = - (amount: number): AppThunk => - (dispatch, getState) => { - const currentValue = selectCount(getState()); - - if (currentValue % 2 === 1 || currentValue % 2 === -1) { - dispatch(incrementByAmount(amount)); - } - }; diff --git a/src/features/quotes/Quotes.module.css b/src/features/quotes/Quotes.module.css deleted file mode 100644 index 13b7b51..0000000 --- a/src/features/quotes/Quotes.module.css +++ /dev/null @@ -1,18 +0,0 @@ -.select { - font-size: 25px; - padding: 2px 5px; - size: 50px; - outline: none; - border: 2px solid transparent; - color: rgb(112, 76, 182); - cursor: pointer; - background-color: rgba(112, 76, 182, 0.1); - border-radius: 5px; - transition: all 0.15s; -} - -.container { - display: flex; - flex-direction: column; - align-items: center; -} diff --git a/src/features/quotes/Quotes.tsx b/src/features/quotes/Quotes.tsx deleted file mode 100644 index f6977fc..0000000 --- a/src/features/quotes/Quotes.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { useState } from "react"; -import styles from "./Quotes.module.css"; -import { useGetQuotesQuery } from "./quotesApiSlice"; - -const options = [5, 10, 20, 30]; - -export const Quotes = () => { - const [numberOfQuotes, setNumberOfQuotes] = useState(10); - // Using a query hook automatically fetches data and returns query values - const { data, isError, isLoading, isSuccess } = useGetQuotesQuery(numberOfQuotes); - - if (isError) { - return ( -
-

There was an error!!!

-
- ); - } - - if (isLoading) { - return ( -
-

Loading...

-
- ); - } - - if (isSuccess) { - return ( -
-

Select the Quantity of Quotes to Fetch:

- - {data.quotes.map(({ author, quote, id }) => ( -
- “{quote}” -
- {author} -
-
- ))} -
- ); - } - - return null; -}; diff --git a/src/features/quotes/quotesApiSlice.ts b/src/features/quotes/quotesApiSlice.ts deleted file mode 100644 index cb74392..0000000 --- a/src/features/quotes/quotesApiSlice.ts +++ /dev/null @@ -1,38 +0,0 @@ -// Need to use the React-specific entry point to import `createApi` -import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"; - -interface Quote { - id: number; - quote: string; - author: string; -} - -interface QuotesApiResponse { - quotes: Quote[]; - total: number; - skip: number; - limit: number; -} - -// Define a service using a base URL and expected endpoints -export const quotesApiSlice = createApi({ - baseQuery: fetchBaseQuery({ baseUrl: "https://dummyjson.com/quotes" }), - reducerPath: "quotesApi", - // Tag types are used for caching and invalidation. - tagTypes: ["Quotes"], - endpoints: (build) => ({ - // Supply generics for the return type (in this case `QuotesApiResponse`) - // and the expected query argument. If there is no argument, use `void` - // for the argument type instead. - getQuotes: build.query({ - query: (limit = 10) => `?limit=${limit}`, - // `providesTags` determines which 'tag' is attached to the - // cached data returned by the query. - providesTags: (result, error, id) => [{ type: "Quotes", id }], - }), - }), -}); - -// Hooks are auto-generated by RTK-Query -// Same as `quotesApiSlice.endpoints.getQuotes.useQuery` -export const { useGetQuotesQuery } = quotesApiSlice;