|
/** Traditional Redux + REST implementation */ |
|
import React from 'react'; |
|
import Redux from 'react-redux'; |
|
|
|
// ################################################# FRONTEND |
|
// FRONTEND 1 - UI: HotDeals.jsx |
|
export default HotDeals = Redux.connect( |
|
(state) => _.pick(state, ["deals", "dealsError", "dealsLoading"]), |
|
{ loadHotDeals } // defined below |
|
)(function HotDeals({deals, dealsError, dealsLoading, loadHotDeals}){ |
|
React.useEffect(() => loadHotDeals(), []) // on mount |
|
if (!deals || dealsLoading) return <p>Loading....</p> |
|
if (dealsError) return <p>Something went wrong</p> |
|
return <ul>{deals.map(deal => <Deal {...deal}/>)}</ul> |
|
}) |
|
|
|
// FRONTEND 2: actions.js |
|
export function loadHotDeals() { |
|
return { |
|
type: "LOAD_HOT_DEALS", |
|
promise: fetchHotDeals() |
|
} |
|
} |
|
|
|
// FRONTEND 3: backend-client.js |
|
export function fetchHotDeals() { |
|
return fetch('https://backend/hot-deals') |
|
.then(res => res.json()) |
|
} |
|
|
|
// FRONTEND 4: reducer.js |
|
//import { handle } from 'redux-pack'; // 1 Promise action -> 4 events |
|
export function reducer(state = myInitialState, action) { |
|
const { type, payload } = action; |
|
switch (type) { |
|
case "LOAD_HOT_DEALS": |
|
// The UI expects deals, dealsLoading, dealsError: |
|
return handle(state, action, { |
|
start: prevState => ({ |
|
...prevState, |
|
dealsLoading: true, dealsError: null |
|
}), |
|
finish: prevState => |
|
({ ...prevState, dealsLoading: false }), |
|
failure: prevState => |
|
({ ...prevState, dealsError: payload }), |
|
success: prevState => |
|
({ ...prevState, deals: payload }) |
|
}); |
|
// ... repeat ∀ data sources ... |
|
} |
|
} |
|
|
|
// ################################################## BACKEND |
|
// BACKEND - BUSINESS: webshop.js |
|
async function hotDeals(env) { return ...; } |
|
|
|
// BACKEND - PLUMBING: controller.js |
|
router.get('/hot-deals', async(req, res) => |
|
res.json(await webshop.hotDeals(req.env))); |