commit f929e5156cb1941beb2f5329e7b9b8fae4c7691b
parent 17c1d3d2070ee0c060c3e71a9e5868f004b2b034
Author: Adrián Oliva <[email protected]>
Date:   Wed, 17 May 2023 10:50:12 -0600

Modified to be minimal.

Diffstat:
Msrc/App.jsx | 51+++------------------------------------------------
Dsrc/App.test.jsx | 15---------------
Msrc/app/store.js | 6+++---
Msrc/features/counter/Counter.jsx | 63++++++++++++---------------------------------------------------
Dsrc/features/counter/Counter.module.css | 78------------------------------------------------------------------------------
Dsrc/features/counter/counterAPI.js | 6------
Msrc/features/counter/counterSlice.js | 68++++++++++----------------------------------------------------------
Dsrc/features/counter/counterSlice.spec.js | 33---------------------------------
Dsrc/logo.svg | 1-
Msrc/main.jsx | 2+-
10 files changed, 29 insertions(+), 294 deletions(-)

diff --git a/src/App.jsx b/src/App.jsx @@ -1,56 +1,11 @@ import React from "react"; -import logo from "./logo.svg"; -import { Counter } from "./features/counter/Counter"; import "./App.css"; +import { Counter } from "./features/counter/Counter"; function App() { return ( - <div className="App"> - <header className="App-header"> - <img src={logo} className="App-logo" alt="logo" /> - <Counter /> - <p> - Edit <code>src/App.js</code> and save to reload. - </p> - <span> - <span>Learn </span> - <a - className="App-link" - href="https://reactjs.org/" - target="_blank" - rel="noopener noreferrer" - > - React - </a> - <span>, </span> - <a - className="App-link" - href="https://redux.js.org/" - target="_blank" - rel="noopener noreferrer" - > - Redux - </a> - <span>, </span> - <a - className="App-link" - href="https://redux-toolkit.js.org/" - target="_blank" - rel="noopener noreferrer" - > - Redux Toolkit - </a> - ,<span> and </span> - <a - className="App-link" - href="https://react-redux.js.org/" - target="_blank" - rel="noopener noreferrer" - > - React Redux - </a> - </span> - </header> + <div> + <Counter /> </div> ); } diff --git a/src/App.test.jsx b/src/App.test.jsx @@ -1,15 +0,0 @@ -import React from "react"; -import { render } from "@testing-library/react"; -import { Provider } from "react-redux"; -import { store } from "./app/store"; -import App from "./App"; - -test("renders learn react link", () => { - const { getByText } = render( - <Provider store={store}> - <App /> - </Provider> - ); - - expect(getByText(/learn/i)).toBeInTheDocument(); -}); diff --git a/src/app/store.js b/src/app/store.js @@ -1,8 +1,8 @@ -import { configureStore } from "@reduxjs/toolkit"; -import counterReducer from "../features/counter/counterSlice"; +import { configureStore } from '@reduxjs/toolkit' +import counterReducer from '../features/counter/counterSlice' export const store = configureStore({ reducer: { counter: counterReducer, }, -}); +}) diff --git a/src/features/counter/Counter.jsx b/src/features/counter/Counter.jsx @@ -1,67 +1,28 @@ -import React, { useState } from "react"; -import { useSelector, useDispatch } from "react-redux"; -import { - decrement, - increment, - incrementByAmount, - incrementAsync, - incrementIfOdd, - selectCount, -} from "./counterSlice"; -import styles from "./Counter.module.css"; +import React from 'react' +import { useSelector, useDispatch } from 'react-redux' +import { decrement, increment } from './counterSlice' export function Counter() { - const count = useSelector(selectCount); - const dispatch = useDispatch(); - const [incrementAmount, setIncrementAmount] = useState("2"); - - const incrementValue = Number(incrementAmount) || 0; + const count = useSelector((state) => state.counter.value) + const dispatch = useDispatch() return ( <div> - <div className={styles.row}> - <button - className={styles.button} - aria-label="Decrement value" - onClick={() => dispatch(decrement())} - > - - - </button> - <span className={styles.value}>{count}</span> + <div> <button - className={styles.button} aria-label="Increment value" onClick={() => dispatch(increment())} > - + + Increment </button> - </div> - <div className={styles.row}> - <input - className={styles.textbox} - aria-label="Set increment amount" - value={incrementAmount} - onChange={(e) => setIncrementAmount(e.target.value)} - /> + <span>{count}</span> <button - className={styles.button} - onClick={() => dispatch(incrementByAmount(incrementValue))} - > - Add Amount - </button> - <button - className={styles.asyncButton} - onClick={() => dispatch(incrementAsync(incrementValue))} - > - Add Async - </button> - <button - className={styles.button} - onClick={() => dispatch(incrementIfOdd(incrementValue))} + aria-label="Decrement value" + onClick={() => dispatch(decrement())} > - Add If Odd + Decrement </button> </div> </div> - ); + ) } diff --git a/src/features/counter/Counter.module.css b/src/features/counter/Counter.module.css @@ -1,78 +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; - background: 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-color: rgba(112, 76, 182, 0.1); - 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/counterAPI.js b/src/features/counter/counterAPI.js @@ -1,6 +0,0 @@ -// A mock function to mimic making an async request for data -export function fetchCount(amount = 1) { - return new Promise((resolve) => - setTimeout(() => resolve({ data: amount }), 500) - ); -} diff --git a/src/features/counter/counterSlice.js b/src/features/counter/counterSlice.js @@ -1,29 +1,10 @@ -import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; -import { fetchCount } from "./counterAPI"; - -const initialState = { - value: 0, - status: "idle", -}; - -// 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. -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; - } -); +import { createSlice } from '@reduxjs/toolkit' export const counterSlice = createSlice({ - name: "counter", - initialState, - // The `reducers` field lets us define reducers and generate associated actions + name: 'counter', + initialState: { + value: 0, + }, reducers: { increment: (state) => { // Redux Toolkit allows us to write "mutating" logic in reducers. It @@ -34,40 +15,11 @@ export const counterSlice = createSlice({ }, 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 - .addCase(incrementAsync.pending, (state) => { - state.status = "loading"; - }) - .addCase(incrementAsync.fulfilled, (state, action) => { - state.status = "idle"; - state.value += action.payload; - }); + } }, -}); - -export const { increment, decrement, incrementByAmount } = counterSlice.actions; - -// The function below is called a selector and allows us to select a value from -// the state. Selectors can also be defined inline where they're used instead of -// in the slice file. For example: `useSelector((state: RootState) => state.counter.value)` -export const selectCount = (state) => state.counter.value; +}) -// 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) => (dispatch, getState) => { - const currentValue = selectCount(getState()); - if (currentValue % 2 === 1) { - dispatch(incrementByAmount(amount)); - } -}; +// Action creators are generated for each case reducer function +export const { increment, decrement } = counterSlice.actions -export default counterSlice.reducer; +export default counterSlice.reducer diff --git a/src/features/counter/counterSlice.spec.js b/src/features/counter/counterSlice.spec.js @@ -1,33 +0,0 @@ -import counterReducer, { - increment, - decrement, - incrementByAmount, -} from "./counterSlice"; - -describe("counter reducer", () => { - const initialState = { - value: 3, - status: "idle", - }; - it("should handle initial state", () => { - expect(counterReducer(undefined, { type: "unknown" })).toEqual({ - value: 0, - status: "idle", - }); - }); - - it("should handle increment", () => { - const actual = counterReducer(initialState, increment()); - expect(actual.value).toEqual(4); - }); - - it("should handle decrement", () => { - const actual = counterReducer(initialState, decrement()); - expect(actual.value).toEqual(2); - }); - - it("should handle incrementByAmount", () => { - const actual = counterReducer(initialState, incrementByAmount(2)); - expect(actual.value).toEqual(5); - }); -}); diff --git a/src/logo.svg b/src/logo.svg @@ -1 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><g fill="#764ABC"><path d="M65.6 65.4c2.9-.3 5.1-2.8 5-5.8-.1-3-2.6-5.4-5.6-5.4h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 1.5.7 2.8 1.6 3.7-3.4 6.7-8.6 11.6-16.4 15.7-5.3 2.8-10.8 3.8-16.3 3.1-4.5-.6-8-2.6-10.2-5.9-3.2-4.9-3.5-10.2-.8-15.5 1.9-3.8 4.9-6.6 6.8-8-.4-1.3-1-3.5-1.3-5.1-14.5 10.5-13 24.7-8.6 31.4 3.3 5 10 8.1 17.4 8.1 2 0 4-.2 6-.7 12.8-2.5 22.5-10.1 28-21.4z"/><path d="M83.2 53c-7.6-8.9-18.8-13.8-31.6-13.8H50c-.9-1.8-2.8-3-4.9-3h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 3 2.6 5.4 5.6 5.4h.2c2.2-.1 4.1-1.5 4.9-3.4H52c7.6 0 14.8 2.2 21.3 6.5 5 3.3 8.6 7.6 10.6 12.8 1.7 4.2 1.6 8.3-.2 11.8-2.8 5.3-7.5 8.2-13.7 8.2-4 0-7.8-1.2-9.8-2.1-1.1 1-3.1 2.6-4.5 3.6 4.3 2 8.7 3.1 12.9 3.1 9.6 0 16.7-5.3 19.4-10.6 2.9-5.8 2.7-15.8-4.8-24.3z"/><path d="M32.4 67.1c.1 3 2.6 5.4 5.6 5.4h.2c3.1-.1 5.5-2.7 5.4-5.8-.1-3-2.6-5.4-5.6-5.4h-.2c-.2 0-.5 0-.7.1-4.1-6.8-5.8-14.2-5.2-22.2.4-6 2.4-11.2 5.9-15.5 2.9-3.7 8.5-5.5 12.3-5.6 10.6-.2 15.1 13 15.4 18.3 1.3.3 3.5 1 5 1.5-1.2-16.2-11.2-24.6-20.8-24.6-9 0-17.3 6.5-20.6 16.1-4.6 12.8-1.6 25.1 4 34.8-.5.7-.8 1.8-.7 2.9z"/></g></svg> diff --git a/src/main.jsx b/src/main.jsx @@ -1,6 +1,6 @@ import React from "react"; import ReactDOM from "react-dom/client"; -import { Provider } from "react-redux"; +import { Provider } from 'react-redux' import { store } from "./app/store"; import App from "./App"; import "./index.css";