Heroes React
May 1, 2026 · View on GitHub
This is a React single-page application (SPA) — a demo/comparison app showing the Tour of Heroes concept in React. It uses a json-server mock backend, Redux with Redux-Saga for state management, and Cypress for end-to-end testing.
Part of a family of comparative apps: Angular, React, Svelte, and Vue.
Repository Structure
heroes-react/
├── .github/
│ ├── copilot-instructions.md # Coding conventions for AI agents
│ ├── workflows/
│ │ ├── ci.yml # PR validation (lint + build)
│ │ └── copilot-setup-steps.yml # Cloud agent environment setup
│ ├── ISSUE_TEMPLATE/ # Bug report and feature request forms
│ ├── PULL_REQUEST_TEMPLATE.md # PR checklist
│ └── dependabot.yml # Dependency update automation
├── cypress/
│ ├── fixtures/ # Test fixture data
│ ├── integration/ # E2E test specs (heroes.spec.js)
│ ├── plugins/ # Cypress plugins
│ └── support/ # Cypress support files
├── public/ # Static assets served by CRA
├── src/
│ ├── components/ # Shared UI components (Modal, NavBar, HeaderBar, etc.)
│ │ └── index.js # Barrel re-export for all shared components
│ ├── heroes/ # Heroes feature module
│ │ ├── Heroes.js # Route container (lazy-loaded)
│ │ ├── HeroList.js # List display component
│ │ ├── HeroDetail.js # Edit/add form component
│ │ └── useHeroes.js # Custom hook bridging Redux to component
│ ├── villains/ # Villains feature module (mirrors heroes/)
│ │ ├── Villains.js
│ │ ├── VillainList.js
│ │ ├── VillainDetail.js
│ │ └── useVillains.js
│ ├── store/ # Redux store, actions, reducers, sagas, API calls
│ │ ├── index.js # combineReducers + barrel re-exports
│ │ ├── config.js # API base URL from env
│ │ ├── action-utils.js # Shared response parsers
│ │ ├── hero.actions.js # Action type constants + action creators
│ │ ├── hero.reducer.js # Heroes + selectedHero reducers
│ │ ├── hero.saga.js # Saga side-effects for hero CRUD
│ │ ├── hero.api.js # Axios HTTP calls for heroes
│ │ ├── villain.actions.js # Action type constants + action creators
│ │ ├── villain.reducer.js # Villains + selectedVillain reducers
│ │ ├── villain.saga.js # Saga side-effects for villain CRUD
│ │ └── villain.api.js # Axios HTTP calls for villains
│ ├── App.js # Root component with routing (class component)
│ ├── About.js # About page
│ ├── index.js # Entry point — Redux store + saga setup + ReactDOM.render
│ └── styles.scss # Global SCSS styles
├── .vscode/
│ ├── launch.json # Chrome debug config (port 8626)
│ └── settings.json # Peacock color + ESLint enabled
├── db.json # json-server seed data (heroes + villains)
├── db.js # Seed data as JS module (used by Cypress)
├── routes.json # json-server route rewrites (/api/* → /*)
├── server.js # Express production server (serves built app)
├── cypress.json # Cypress config (port 8626)
├── Dockerfile # Multi-stage Docker build (Node 10 → production)
├── docker-compose.yml # Docker compose for running the app
├── docker-compose.debug.yml # Docker compose debug override
├── .eslintrc.json # ESLint config (airbnb-based + prettier)
├── .prettierrc # Prettier config (single quotes, 2-space tabs)
├── .env # Production env (port 8626, Azure API URL)
├── .env.development # Dev env (port 8626, proxied API)
├── package.json # Dependencies, scripts, proxy config
└── README.md # Project overview and getting started
Tech Stack
- Language: JavaScript (ES2018, JSX)
- Framework: React 16 (class components + hooks)
- State management: Redux 4 + Redux-Saga
- Routing: React Router v5 with lazy-loaded routes
- HTTP client: Axios
- CSS: Bulma + SASS + Font Awesome
- Mock backend: json-server on port 8627 with route rewriting
- Bundler: Create React App (react-scripts 3.x)
- Production server: Express (server.js)
- Container: Docker multi-stage build
Build & Run
# Install dependencies
npm install
# Run frontend + json-server backend concurrently
npm run quick
# Frontend only (requires separate API)
npm start
# Backend only (json-server on port 8627)
npm run backend
# Production build
npm run build
# Production server (serves built app on port 8626)
node server.js
Ports:
8626— React dev server / production Express server8627— json-server mock API
The CRA proxy in package.json forwards /api/* requests from port 8626 to json-server on port 8627 during development.
Testing
E2E tests (Cypress):
# Open Cypress test runner (interactive)
npm run cypress
# Run everything (app + backend + Cypress)
npm run e2e
- Tests live in
cypress/integration/ - Cypress uses the same seed data from
db.jsand resets viajson-server-reset - Configured in
cypress.jsonwith port 8626
Unit tests (react-scripts):
npm test
Uses the built-in CRA test runner (Jest). No unit test files currently exist.
Key Patterns and Conventions
- Feature modules — heroes and villains each get their own directory under
src/with a container component, list, detail, and a custom hook - Redux pattern — each feature has four store files:
*.actions.js(constants + creators),*.reducer.js,*.saga.js(side effects),*.api.js(HTTP calls) - Barrel exports —
src/store/index.jsre-exports all actions, reducers, and sagas;src/components/index.jsre-exports all shared components - Action type naming —
'[Feature] VERB_NOUN'format (e.g.,'[Heroes] LOAD_HERO') - Custom hooks —
useHeroesanduseVillainsencapsulateuseSelector+useDispatch, providing a clean API to container components - Lazy loading — feature modules are code-split with
React.lazy()andSuspense - API config — base URL comes from
REACT_APP_APIenvironment variable viasrc/store/config.js
Adding a New Feature Module
To add a new entity (e.g., "sidekicks"):
- Create seed data — add a
"sidekicks"array todb.jsonanddb.js - Add route rewrite — add
"/sidekick": "/sidekicks"toroutes.json - Create store files in
src/store/:sidekick.actions.js— action types and creators following the[Sidekicks] VERB_NOUNpatternsidekick.api.js— Axios CRUD functions using the API configsidekick.reducer.js— reducer + selectedSidekickReducersidekick.saga.js— saga watchers and workers for each action
- Register in store — update
src/store/index.js:- Add
sidekicksReducerandselectedSidekickReducertocombineReducers - Re-export all new modules (
export * from './sidekick.actions', etc.)
- Add
- Run the saga — add
sagaMiddleware.run(sidekickSaga)insrc/index.js - Create feature directory
src/sidekicks/:Sidekicks.js— container componentSidekickList.js— list displaySidekickDetail.js— edit/add formuseSidekicks.js— custom hook
- Add route — add
<Route path="/sidekicks" component={Sidekicks} />inApp.jswith lazy import - Add nav link — update
src/components/NavBar.js - Add Cypress tests — create
cypress/integration/sidekicks.spec.js
Screen Size / Responsive Rules
- Bulma handles responsive layout via its grid system (
columns,column) - The app uses a sidebar nav (
NavBar) + main content area pattern - No custom breakpoints — relies on Bulma defaults
Common Pitfalls
- Must run both servers —
npm run quickstarts both the React dev server and json-server. Runningnpm startalone will fail API calls - json-server reset — Cypress tests reset data via
json-server-resetPOST to/api/reset. If tests fail with stale data, restart json-server - Port conflicts — the app expects 8626 (frontend) and 8627 (backend) to be free
- node-sass version —
node-sass@4.xrequires Node.js < 15. Use Node 14 for local development - Barrel re-exports — when adding new shared components, update
src/components/index.js; when adding new store modules, updatesrc/store/index.js - Docker image is stale — the Dockerfile uses
node:10.13-alpinewhich is very old; update if building Docker images