관리 메뉴

Dev Blog

(Youtube)Amazon Clone Project(Day1) 본문

Projects/Amazon Clone

(Youtube)Amazon Clone Project(Day1)

Nomad Kim 2021. 4. 24. 03:13

아마존 클론 코딩 목적

1. 비즈니스 모델인 이커머스 웹사이트의 구현 방법 학습

 

  1) Comprehensive Layout & CSS

  2) Login & Authentication

  3) Payment Process

  4) Order History

 

2. 해당 프로젝트로 Redux 재 학습

3. 이커머스 관련 프로젝트 진행을 위한 코드 구조 및 구현 프로세스 파악


금일 진행 사항

 

[Part1]Getting Set Up

 

    - Set up new react app and firebase

 

[Part2]The Home Page
    
    - Made Header and Home page
    - Amazon styled-CSS and layout applied

 

[Part3]The Checkout Page
    
    - Design Checkout Page
    - Set up Redux(reducer, state provider etc)
    - Make 'ADD TO CART' function in reducer

 

 [Part3.5]More Checkout Page
  
    - Design Shopping Basket
    - Add Remove Button function by using dispatch and reducer

 

 


[Part3]The Checkout Page - Set up Redux(reducer, state provider etc)

 

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {StateProvider} from './StateProvider'
import reducer, {initialState} from './reducer'

ReactDOM.render(
  <React.StrictMode>
    <StateProvider reducer={reducer} initialState ={initialState}>
      <App />  
    </StateProvider>
  </React.StrictMode>,
  document.getElementById('root')
);

reportWebVitals();

 

import React, {createContext, useContext, useReducer} from 'react'
//Prepares the dataLayer
export const StateContext = createContext();
// Wrap our app and provide the Data layer to every components
export const StateProvider = ({reducer, initialState, children}) => {
  return <StateContext.Provider value={useReducer(reducer, initialState)}>{children}</StateContext.Provider>
}
//Pull information from the data layer
export const useStateValue = () => useContext(StateContext)

1) createContext

- context를 이용하면 단계마다 일일이 props를 넘겨주지 않고도 컴포넌트 트리 전체에 데이터를 제공할 수 있습니다.

 

2) Context.Provider

<MyContext.Provider value={/* 어떤 값 */}>

- Context 오브젝트에 포함된 React 컴포넌트인 Provider는 context를 구독하는 컴포넌트들에게 context의 변화를 알리는 역할을 합니다.

- Provider 컴포넌트는 value prop을 받아서 이 값을 하위에 있는 컴포넌트에게 전달합니다. 값을 전달받을 수 있는 컴포넌트의 수에 제한은 없습니다. 

- Provider 하위에서 context를 구독하는 모든 컴포넌트는 Provider의 value prop가 바뀔 때마다 다시 렌더링 됩니다.

 

3) useReducer

const [state, dispatch] = useReducer(reducer, initialArg, init);

- useState의 대체 함수입니다. (state, action) => newState의 형태로 reducer를 받고 dispatch 메서드와 짝의 형태로 현재 state(데이터)를 반환합니다. 

- 다수의 하윗값을 포함하는 복잡한 정적 로직을 만드는 경우나 다음 state가 이전 state에 의존적인 경우에 보통 useState보다 useReducer를 선호합니다. 또한 useReducer는 자세한 업데이트를 트리거 하는 컴포넌트의 성능을 최적화할 수 있게 하는데, 이것은 콜백 대신 dispatch를 전달 할 수 있기 때문입니다.

 

4) useContext

const value = useContext(MyContext);

- context 객체(React.createContext에서 반환된 값)을 받아 그 context의 현재 값을 반환합니다. 

- 컴포넌트에서 가장 가까운 <MyContext.Provider>가 갱신되면 이 Hook은 그 MyContext provider에게 전달된 가장 최신의 context value(데이터)를 사용하여 렌더러를 트리거 합니다. 

 

출처: ko.reactjs.org/docs/context.html

 

 

reducer.js

export const initialState = {
  basket: [],
  user: null
}

//Selector
export const getBasketTotal = (basket) => 
  basket?.reduce((amount, item) => item.price + amount, 0);
//? function of '?'

const reducer = (state, action) => {
  // console.log(action)
  // console.log(state)
  switch(action.type) {
    case 'ADD_TO_BASKET':
      return {
        ...state, 
        basket: [...state.basket, action.item],
      };

    case 'REMOVE_FROM_BASKET':
      const index = state.basket.findIndex(
        (basketItem) => basketItem.id === action.id
      )

      let newBasket = [...state.basket]

      if(index >=0) {
        newBasket.splice(index, 1)
      } else {
        console.warn(`Can't remove prodect (id:${action.id}) as it is not in basket!!`)
      }
      return {
        ...state,
        basket: newBasket
      }

    default:
      return state;
  }
}

export default reducer;

Product.js(ADD TO BASKET)

import React from 'react'
import './Product.css'
import { useStateValue } from './StateProvider'

function Product({id, title, image, price, rating}) {

  const [, dispatch] = useStateValue(); //state = {basket:Array(2)}

  const addToBasket = () =>{
    //dispatch the item into the data layer
    dispatch({
      type: 'ADD_TO_BASKET',
      item: {
        id: id, 
        title:title,
        image: image,
        price: price,
        rating: rating
      }
    })
  }

  return (
    <div className="product">
      <div className="product__info">
        <p>{title}</p>
        <p className="product__price">
          <small>$</small>
          <strong>{price}</strong>
        </p>
        <div className="product__rating">
          {Array(rating).fill().map((_, i)=>(
            <p key={i}>🌟</p>
          ))} 
        </div>
      </div>
      <img src={image} alt=""></img>
      <button onClick={addToBasket}>Add to Basket</button>
    </div>
  )
}

export default Product

CheckoutProduct.js(REMOVE FROM BASKET)

import React from 'react'
import './CheckoutProduct.css'
import {useStateValue} from './StateProvider'

function CheckoutProduct({id, image, title, price, rating, hideButton}) {

  const [, dispatch] = useStateValue();

  const removeFromBasket = (e) =>{
    dispatch({
      type: 'REMOVE_FROM_BASKET',
      id: id
    })
  }

  return (
    <div className="checkoutProduct">
      <img className="checkoutProduct__image" src={image} alt=""/>
      <div className="checkoutProduct__info">
        <p className="checkoutProduct__title">{title}</p>
        <p className="checkoutProduct__price">
          <small>$</small>
          <strong>{price}</strong>
        </p>
        <div className="checkoutProduct__rating">
          {Array(rating).fill().map((_, i) => (<p>🌟</p>))}
        </div>
        {hideButton ? null : <button onClick={removeFromBasket}>remove from basket</button>}
      </div>
    </div>
  )
}

export default CheckoutProduct

리덕스 개념 설명 코드

const { createStore } = require("redux");

//!초기 state(데이터)를 정의한다.
const initState = {
  name: "김코딩",
  posts: [],
};

//!action creator
//!action 은 객체로 data 를 받으면 type 과 payload(여기에서 data) 가 reducer 에게 전달된다.
const changeUsername = (data) => {
  return {
    type: "CHANGE_NAME",
    data,
  };
};

const addPost = (post) => {
  return {
    type: "ADD_POST",
    post,
  };
};
//! reducer: 실제 메소드로 데이터를 변경해준다. 
//! initState(prevState)와 action 을 받아 type 에 따라 
//! payload를 활용하여 특정 method 를 실행시킨다. 
const reducer = (prevState, action) => {
  switch (action.type) {
    case "CHANGE_NAME":
      return {
        ...prevState,
        name: action.data,
      };
    case "ADD_POST":
      return {
        ...prevState,
        posts: [...prevState.posts, action.post],
      };
    default:
      return prevState;
  }
};

//! Store
//! createStore 메소드를 활용. createStore(reducer, [preloadedState], [enhancer])
//! reducer: returns the next state tree, given the current state tree and an action to handle.
//! [preloadedState]: initState  
//! [enhandcer]: to enhance the store with third-party capabilities such as middleware, time travel, persistence, etc.
const store = createStore(reducer, initState);

//! dispatch
//! The base dispatch function always synchronously sends an action to the store's reducer, 
//! along with the previous state returned by the store, to calculate a new state. 
//! It expects actions to be plain objects ready to be consumed by the reducer.
store.dispatch(changeUsername("김제이"));
store.dispatch(addPost("포스트1"));
store.dispatch(addPost("포스트2"));
console.log(store.getState());

 

Lecture from:

youtu.be/RDV3Z1KCBvo

medium.com/cleverprogrammer/amazon-clone-using-react-the-ultimate-guide-fba2b36f3458

'Projects > Amazon Clone' 카테고리의 다른 글

(Youtube)Amazon Clone Project(Day3)  (0) 2021.04.26
(Youtube)Amazon Clone Project(Day2)  (0) 2021.04.26
Comments