Skip to content

garypotato/Online-shopping-app

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

A Mobile Shopping App


The purpose of developing this SPA app is practising Redux-middleware.
I always use Function Components in web development, in this app I used Class Component because I believe some developers are still using it so it is a good opportunity to do some practice.
Please visit this app in Mobile Mode 👉 https://main.d24qeurrp3ff0q.amplifyapp.com/

UI

This is the home page of this app. I like the user interface as it is simple and easy to understand.
1

Mock Data

All the mock data are stored under the Public/mock file which is easy to access.
Products are stored in an Array, and the structure of the data will be like that

[
  {
    "id": "p-100",
    "shopIds": ["s-100"],
    "shop": "Dancing club",
    "tag": "noBooking",
    "product": "DancingClub",
    "currentPrice": 1,
    "oldPrice": 400,
    "picture": "https://p1.meituan.net/dpdeal/a8eb71748e1f4df175668368e98bb4f868511.jpg.webp@120w_90h_1e_1c_1l|watermark=1&&r=1&p=9&x=20&y=20"
  },
  {...},
  {...}
 ]

Array allows me to map and re-structure the data easily. The new data stored in Object will be like that WeChat Screenshot_20210820151509
Object allows me to use Object.keys() or Object.values() to get whatever I need. The data structure of shops and orders will be the same.

Fetch API

Axios is definetly the best option but I insisted using Fetch, because it is built into most modern browsers and no installation is required as such. Another reason is that I don't need XSRF protection and interceptor.

const headers = new Headers({
  "Accept": "application/json",
  "Content-Type": "application/json"
})

function get(url) {
  return fetch(url, {
    method: "GET",
    headers: headers
  }).then(response => {
    return handleResponse(url, response);
  }).catch(error => {
    return Promise.reject({error: {message: "Request failed."}})
  })
}

function post(url, data) {
  return fetch(url, {
    method: "POST",
    headers: headers,
    body: data
  }).then(response => {
    return handleResponse(url, response);
  }).catch(error => {
    return Promise.reject({error: {message: "Request failed."}})
  })
}

What I need to just make sure is that it provide and accept entities as JSON.

Why React-redux

useContext + useReducer is becoming more and more popular but I stick with React-redux. It provides many API can make the code more readable and easy to understand. Connet() can easily connect the React component to Redux which is awesome for this application.

Data Structure

The data can divided into to two parts. The first part is entites, which stored product details, shope details, user orders and search history. The second part is the state based on each pages like home, producet detils, login, user information, purchase and so on.
WeChat Screenshot_20210820195127

entities

entities includes multiple state, and each state has its own schema which describes the shape of the state.
WeChat Screenshot_20210820195834 WeChat Screenshot_20210820200122 WeChat Screenshot_20210820200229
After receiving response, the response data will be normalized and return a new form

const normalizeData = (data, schema) => {
  const {id, name} = schema
  let kvObj = {}
  let ids = []
  if(Array.isArray(data)) {
    data.forEach(item => {
      kvObj[item[id]] = item
      ids.push(item[id])
    })
  } else {
    kvObj[data[id]] = data
    ids.push(data[id])
  }
  return {
    [name]: kvObj,
    ids
  }
}

[name]: kvObj will be the state for [name] entity(for example 'shop') while ids array will be the state for different pages like home page.
The state stored in entity
WeChat Screenshot_20210820202026 WeChat Screenshot_20210820202059
The state stored in pages, for example home pages. In this page, I just need to know which item is under discount or user will like. Therefore, an array of id is the best way to let the app know which item should go which section.
WeChat Screenshot_20210820202340

other state

Beside entities, the rest of state will be divided to multiple parts according to the pages.

home - item id array
detail - selected item detail
login - if user is login the app user information
user - user's order

Redux Middelware

The reason I used redux-middleware is DRY Priciple. In this app, same logic will go again and again and I am tired of writing the same code. Another important thing is it is hard to maintain so I decieded to use middleware.

define an action

The action is named in [FETCH_DATA]. endpoint will be the url while the schema describes the shape of the data will be.

{
  [FETCH_DATA]: {
    types: [
      types.FETCH_LIKES_REQUEST,
      types.FETCH_LIKES_SUCCESS,
      types.FETCH_LIKES_FAILURE
    ],
    endpoint,
    schema
  }
}

check the action in middleware

This middleware will be called when an action comes with [FETCH_DATA]

const callAPI = action[FETCH_DATA]
  if(typeof callAPI === 'undefined') {
    return next(action)
  }

  const { endpoint, schema, types } = callAPI
  if(typeof endpoint !== 'string') {
    throw new Error('endpoint must be a string type URL')
  }
  if(!schema) {
    throw new Error('undefined schema')
  }
  if(!Array.isArray(types) && types.length !== 3) {
    throw new Error('must be an array with 3 action types')
  }
  if(!types.every(type => typeof type === 'string')) {
    throw new Error('action type must be string type')
  }

pass to next middleware

const actionWith = data => {
    const finalAction = {...action, ...data}
    delete finalAction[FETCH_DATA]
    return finalAction
  }

  const [requestType, successType, failureType] = types

  next(actionWith({type: requestType}))
  return fetchData(endpoint, schema).then(
    response => next(actionWith({
      type: successType,
      response 
    })),
    error => next(actionWith({
      type: failureType,
      error: error.message || 'data fetching failed'
    }))
  )

Enhance user experience

when user scroll to the bottom, it will immediately fetch new data. 2

AWS Amplify

The most important reason I am using AWS Amplify is it's easy to use. The Amplify Console provides a continuous delivery and hosting service for web applications, which means front-end developer doesn't need to know much about web app deployment while AWS Amplify will do everything for you.

About

Mobile-first online shopping application. React, Redux-middleware.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published