From 5db0d0afc1683308e3a2325cc1160d4c44459824 Mon Sep 17 00:00:00 2001 From: Branden Jones Date: Wed, 24 Apr 2024 15:21:01 -0400 Subject: [PATCH] redux and thunk, no API yet --- cypress/e2e/listing-restaurants.cy.js | 6 +- package-lock.json | 108 +++++++++++++++++++++++++- package.json | 7 +- src/App.js | 14 ++-- src/components/RestaurantList.js | 14 +++- src/components/RestaurantList.spec.js | 36 +++++---- src/components/RestaurantScreen.js | 6 +- src/store/index.js | 7 ++ src/store/reducers.js | 4 + src/store/restaurants/actions.js | 1 + src/store/restaurants/reducers.js | 9 +++ 11 files changed, 176 insertions(+), 36 deletions(-) create mode 100644 src/store/index.js create mode 100644 src/store/reducers.js create mode 100644 src/store/restaurants/actions.js create mode 100644 src/store/restaurants/reducers.js diff --git a/cypress/e2e/listing-restaurants.cy.js b/cypress/e2e/listing-restaurants.cy.js index a51d23a..b85746d 100644 --- a/cypress/e2e/listing-restaurants.cy.js +++ b/cypress/e2e/listing-restaurants.cy.js @@ -1,7 +1,5 @@ -describe('Listing Restaurants', () => -{ - it('shows restaurants from the server', () => - { +describe('Listing Restaurants', () => { + it('shows restaurants from the server', () => { const sushiPlace = "Sushi Place"; const pizzaPlace = "Pizza Place"; diff --git a/package-lock.json b/package-lock.json index 1efe804..d3b113e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,10 @@ "devDependencies": { "@testing-library/user-event": "^14.4.3", "cypress": "^10.9.0", - "jest": "^27.5.1" + "jest": "^27.5.1", + "react-redux": "^8.0.4", + "redux": "^4.2.0", + "redux-thunk": "^2.4.1" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -4474,6 +4477,16 @@ "@types/node": "*" } }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", + "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "dev": true, + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -4712,6 +4725,12 @@ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==" }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==", + "dev": true + }, "node_modules/@types/ws": { "version": "8.5.10", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", @@ -10125,6 +10144,21 @@ "he": "bin/he" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dev": true, + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, "node_modules/hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -16776,6 +16810,51 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "node_modules/react-redux": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.4.tgz", + "integrity": "sha512-yMfQ7mX6bWuicz2fids6cR1YT59VTuT8MKyyE310wJQlINKENCeT1UcPdEiX6znI5tF8zXyJ/VYvDgeGuaaNwQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", + "hoist-non-react-statics": "^3.3.2", + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + }, + "peerDependencies": { + "@types/react": "^16.8 || ^17.0 || ^18.0", + "@types/react-dom": "^16.8 || ^17.0 || ^18.0", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0", + "react-native": ">=0.59", + "redux": "^4" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/react-redux/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, "node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -16911,6 +16990,24 @@ "node": ">=8" } }, + "node_modules/redux": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", + "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, + "node_modules/redux-thunk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz", + "integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==", + "dev": true, + "peerDependencies": { + "redux": "^4" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", @@ -19175,6 +19272,15 @@ "requires-port": "^1.0.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "dev": true, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index e09fbd2..1e4c712 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,9 @@ "devDependencies": { "@testing-library/user-event": "^14.4.3", "cypress": "^10.9.0", - "jest": "^27.5.1" + "jest": "^27.5.1", + "react-redux": "^8.0.4", + "redux": "^4.2.0", + "redux-thunk": "^2.4.1" } -} \ No newline at end of file +} diff --git a/src/App.js b/src/App.js index d3f1461..e11bea6 100644 --- a/src/App.js +++ b/src/App.js @@ -1,10 +1,12 @@ +import { Provider } from 'react-redux'; +import store from './store'; import RestaurantScreen from './components/RestaurantScreen'; -export default function App() -{ - return - ( -
+ +export default function App(){ + + return ( + -
+ ) } \ No newline at end of file diff --git a/src/components/RestaurantList.js b/src/components/RestaurantList.js index 578580c..62ee6f8 100644 --- a/src/components/RestaurantList.js +++ b/src/components/RestaurantList.js @@ -1,7 +1,8 @@ import { useEffect } from "react"; +import { connect } from "react-redux"; +import { loadRestaurants } from '../store/restaurants/actions'; -export default function RestaurantList({ loadRestaurants, restaurants }) -{ +export function RestaurantList({ loadRestaurants, restaurants }) { useEffect(() => { loadRestaurants(); @@ -14,4 +15,11 @@ export default function RestaurantList({ loadRestaurants, restaurants }) ))} ); -}; \ No newline at end of file +}; + +const mapStateToProps = state => ({ + restaurants: state.restaurants.records, +}); +const mapDispatchToProps = {loadRestaurants}; + +export default connect(mapStateToProps, mapDispatchToProps)(RestaurantList); \ No newline at end of file diff --git a/src/components/RestaurantList.spec.js b/src/components/RestaurantList.spec.js index ac23b19..0e74dec 100644 --- a/src/components/RestaurantList.spec.js +++ b/src/components/RestaurantList.spec.js @@ -1,24 +1,28 @@ import { render, screen } from '@testing-library/react'; -import RestaurantList from './RestaurantList'; +import { RestaurantList } from './RestaurantList'; -describe('RestaurantList', () => -{ - it('loads restaurants on first render', () => - { - const loadRestaurants = jest.fn().mockName('loadRestaurnats'); - const restaurants = []; - render(); - expect(loadRestaurants).toHaveBeenCalled(); - }); - it('displays the restaurants', () => - { - const noop = () => {}; //"no operation" - const restaurants = - [ +describe('RestaurantList', () => { + let loadRestaurants; + const restaurants = [ {id: 1, name: 'Sushi Place'}, {id: 2, name: 'Pizza Place'} ]; - render(); + function renderComponent(){ + loadRestaurants = jest.fn().mockName('loadRestaurnats'); + render( + + ) + } + + it('loads restaurants on first render', () => { + renderComponent(); + expect(loadRestaurants).toHaveBeenCalled(); + }); + it('displays the restaurants', () => { + renderComponent(); expect(screen.getByText('Sushi Place')).toBeInTheDocument(); expect(screen.getByText('Pizza Place')).toBeInTheDocument(); }) diff --git a/src/components/RestaurantScreen.js b/src/components/RestaurantScreen.js index bba4e94..5806768 100644 --- a/src/components/RestaurantScreen.js +++ b/src/components/RestaurantScreen.js @@ -1,9 +1,7 @@ import RestaurantList from "./RestaurantList"; -export default function RestaurantScreen() -{ - return - ( +export default function RestaurantScreen() { + return (

Restaurants

diff --git a/src/store/index.js b/src/store/index.js new file mode 100644 index 0000000..49d547c --- /dev/null +++ b/src/store/index.js @@ -0,0 +1,7 @@ +import { createStore, applyMiddleware } from 'redux'; +import thunk from 'redux-thunk'; +import rootReducer from './reducers'; + +const store = createStore(rootReducer, applyMiddleware(thunk)); + +export default store; \ No newline at end of file diff --git a/src/store/reducers.js b/src/store/reducers.js new file mode 100644 index 0000000..36cdb69 --- /dev/null +++ b/src/store/reducers.js @@ -0,0 +1,4 @@ +import { combineReducers } from 'redux'; +import restaurants from './restaurants/reducers'; + +export default combineReducers({restaurants}); \ No newline at end of file diff --git a/src/store/restaurants/actions.js b/src/store/restaurants/actions.js new file mode 100644 index 0000000..b0e7a50 --- /dev/null +++ b/src/store/restaurants/actions.js @@ -0,0 +1 @@ +export const loadRestaurants = () => () => {}; \ No newline at end of file diff --git a/src/store/restaurants/reducers.js b/src/store/restaurants/reducers.js new file mode 100644 index 0000000..ed8b84e --- /dev/null +++ b/src/store/restaurants/reducers.js @@ -0,0 +1,9 @@ +import { combineReducers } from 'redux'; + +function records() { + return[]; +} + +export default combineReducers({ + records, +}) \ No newline at end of file