ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 아마존 E-commerce 클론 -21) Admin product관리페이지 만들기
    NODE.JS 2021. 5. 28. 21:56

    먼저 상품관리페이지를 만들자 .

     

    프로덕스 리스트 스크린을 만든다. 

     

    ProductListScreen.js

    import React, { useEffect } from 'react'
    import { useSelector, useDispatch } from 'react-redux'
    import { listProducts } from '../actions/productActions';
    import LoadingBox from '../components/LoadingBox';
    import MessageBox from '../components/MessageBox';
    
    function ProductListScreen(props) {
        const productList = useSelector(state=>state.productList);
        const {loading, error, products}= productList;
    
        const dispatch = useDispatch()
        useEffect(()=>{
            dispatch(listProducts())
        },[dispatch])
    
        const deleteHandler = (product)=>{
    
        }
        return (
            <div>
                <h1>Products</h1>
                {loading? <LoadingBox></LoadingBox>:
                error? <MessageBox variant='danger' >{error}</MessageBox>:
                <table className="table">
                    <thead>
                        <tr>
                            <th>ID</th>
                            <th>NAME</th>
                            <th>PRICE</th>
                            <th>CATEGORY</th>
                            <th>BRAND</th>
                            <th>ACTIONS</th>
                        </tr>
                    </thead>
                    <tbody>
                        {products.map(product=>(
                            <tr key = {product._id}>
                                <td>{product._id}</td>
                                <td>{product.name}</td>
                                <td>{product.price}</td>
                                <td>{product.category}</td>
                                <td>{product.brand}</td>
                                <td>
                                    <button type="button" className="small" onClick={()=>props.history.push(`/product/${product._id}/edit`)}>
                                        Edit
                                    </button>
                                    <button className="small" type="button" onClick={()=>deleteHandler(product)}>Delete</button>
                                </td>
                            </tr>
                        ))}
                    </tbody>
                </table>
                }
            </div>
        )
    }
    
    export default ProductListScreen
    

     

    App.js에 먼저 만든 페이지들을 설정해주자. 

    여기서 페이지들은 관리자일때만 들어갈 수 있다. 

    import ProductListScreen from './screens/ProductListScreen';
    
    	<AdminRoute path="/productlist" component={ProductListScreen} exact></AdminRoute>
    

     

     

    이제 페이지내에 create product버튼을 만들고, 

    버튼을 누르면 edit페이지로 이동하는 것까지 구현해보자. 

    (백엔드에서 새로운 상품 생성한다)

     

     

     

    product router에 추가(이름 중복 방지를 위해서 Date.now()를 추가했다)

    productRouter.post('/', isAuth, isAdmin, expressAsyncHandler(async(req, res)=>{
        const product =  new Product({
            name: 'samle name'+Date.now(),
            image:'/images/p1.jpeg',
            price:0,
            category:'sample caategory',
            brand:'sample brand',
            countInStock: 0,
            rating: 2,
            numReviews:0,
            description:'sample description'
    
        })
        const createdProduct = await product.save();
        res.send({message:'Product Created', product: createdProduct})
    }))
    export default productRouter;

     

    상품리스트페이지에 createProduct 버튼을 추가한다. 

    return (
            <div>
                <div className="row">
                    <h1>Products</h1>
                    <button className="primary" type="button" onClick={createHandler}>Create Product</button>
                </div>

     

    이제 리덕스 이용해서 만들어보자. 

    productConstants.js

    export const PRODUCT_CREATE_REQUEST = 'PRODUCT_CREATE_REQUEST';
    export const PRODUCT_CREATE_SUCCESS = 'PRODUCT_CREATE_SUCCESS';
    export const PRODUCT_CREATE_FAIL = 'PRODUCT_CREATE_FAIL';
    export const PRODUCT_CREATE_RESET = 'PRODUCT_CREATE_RESET';
    

    productActions.js

    여기서 data.product는 라우터에서 돌려보내준 message, product 중에서 product를 가져다 쓴다는 의미다. 

    export const createProduct = () =>async(dispatch, getState)=>{
        dispatch({
            type: PRODUCT_CREATE_REQUEST
        })
        const {userSignin:{userInfo}} = getState();
    
        try{
            const {data} = await Axios.post('/api/products',{}, {
                headers: {Authorization : `Bearer ${userInfo.token}`}
            } )
            dispatch({
                type:PRODUCT_CREATE_SUCCESS,
                payload:data.product
            })
        }catch(error){
            dispatch({type:PRODUCT_CREATE_FAIL, payload:error.response && error.response.data.message? error.response.data.message:error.message})
        }
    }

    productReducers.js

    export const productCreateReducer = (state={}, action)=>{
        switch(action.type){
            case PRODUCT_CREATE_REQUEST:
                return {loading:true}
            case PRODUCT_CREATE_SUCCESS:
                return {loading:false, success:true, product:action.payload}
            case PRODUCT_CREATE_FAIL:
                return {loading:false, error:action.payload}
            case PRODUCT_CREATE_RESET:
                return {};
            default:
                return state;
        }
    }

     

    store.js

    const reducer = combineReducers({
        productList: productListReducer,
        productDetails : productDetailsReducer,
        cart:cartReducer,
        userSignin: userSigninReducer,
        userRegister: userRegisterReducer,
        orderCreate:orderCreateReducer,
        orderDetails:orderDetailsReducer,
        orderPay: orderPayReducer,
        orderMineList:orderMineListReducer,
        userDetails:userDetailsReducer,
        userUpdateProfile:userUpdateProfileReducer,
        productCreate:productCreateReducer
    })

     

    productLIstScreen.js

    import React, { useEffect } from 'react'
    import { useSelector, useDispatch } from 'react-redux'
    import { createProduct, listProducts } from '../actions/productActions';
    
    import { PRODUCT_CREATE_RESET } from '../constants/productConstants';
    
    function ProductListScreen(props) {
        const productList = useSelector(state=>state.productList);
        const {loading, error, products}= productList;
        
        const productCreate = useSelector(state => state.productCreate)
        const {loading:loadingCreate, error:errorCreate, success:successCreate, product:createdProduct} = productCreate
    ...
    
        const createHandler = ()=>{
            dispatch(createProduct());
        }
        useEffect(() => {
            if(successCreate){
                dispatch({
                    type:PRODUCT_CREATE_RESET
                })
                props.history.push(`/product/${createdProduct._id}/edit`)
            }
            dispatch(listProducts())
        }, [createdProduct, dispatch, props.history, successCreate])
    
        return (
        
        <div className="row">
                    <h1>Products</h1>
                    <button className="primary" type="button" onClick={createHandler}>Create Product</button>
                </div>
                {loadingCreate && <LoadingBox></LoadingBox>}
                {errorCreate &&<MessageBox variant='danger'>{errorCreate}</MessageBox>}
                {loading? <LoadingBox></LoadingBox>:
                error? <MessageBox variant='danger' >{error}</MessageBox>:
                <table className="table">

     

     

    create Product하면 아이디를 포함한 edit 페이지로 넘어가는 것을 확인할 수 있다. 

     

    다시 상품리스트로 가면 새로 생성된 것을 확인할 수 있다. 

     

    edit스크린을 만들어보자. 

    import React, { useEffect, useState } from 'react'
    import { useDispatch, useSelector } from 'react-redux'
    import { detailsProduct } from '../actions/productActions'
    import LoadingBox from '../components/LoadingBox'
    import MessageBox from '../components/MessageBox'
    
    function ProductEditScreen(props) {
        const productId = props.match.params.id
        const [name, setName] = useState('')
        const [price, setPrice] = useState('')
        const [category, setCategory] = useState('')
        const [image, setImage] = useState('')
        const [brand, setBrand] = useState('')
        const [description, setDescription] = useState('')
        const [countInStock, setCountInStock] = useState('')
        
        const productDetails = useSelector(state => state.productDetails)
        const {loading, error, product} =productDetails
        const dispatch = useDispatch()
        useEffect(() => {
            if(!product){//만약 상품을 가져오지 못했으면 백엔드에서 불러온다. 
                dispatch(detailsProduct(productId))
            }else{//만약 있으면 
                setName(product.name);
                setPrice(product.price);
                setCategory(product.category);
                setImage(product.image);
                setDescription(product.description);
                setBrand(product.brand);
                setCountInStock(product.countInStock);
            }
        }, [product, dispatch, productId])
    
        const submitHandler= (e)=>{
            e.preventDefault();
        }
        return (
            <div>
                <form onSubmit= {submitHandler} className="form">
                    <div>
                        <h1>EditProdcut {productId}</h1>
                    </div>
                    {loading? <LoadingBox></LoadingBox> :
                    error? <MessageBox variant='danger'>{error}</MessageBox>:
                        <>
                            <div>
                                <label htmlFor="name">Name</label>
                                <input
                                    id="name"
                                    type="text"
                                    placeholder="Enter name"
                                    value={name}
                                    onChange={(e) => setName(e.target.value)}
                                ></input>
                            </div>
                            <div>
                                <label htmlFor="price">Price</label>
                                <input
                                    id="price"
                                    type="text"
                                    placeholder="Enter price"
                                    value={price}
                                    onChange={(e) => setPrice(e.target.value)}
                                ></input>
                            </div>
                            <div>
                                <label htmlFor="image">Image</label>
                                <input
                                    id="image"
                                    type="text"
                                    placeholder="Enter image"
                                    value={image}
                                    onChange={(e) => setImage(e.target.value)}
                                ></input>
                            </div>
                            <div>
                                <label htmlFor="category">Category</label>
                                <input
                                    id="category"
                                    type="text"
                                    placeholder="Enter category"
                                    value={category}
                                    onChange={(e) => setCategory(e.target.value)}
                                ></input>
                            </div>
                            <div>
                                <label htmlFor="brand">Brand</label>
                                <input
                                    id="brand"
                                    type="text"
                                    placeholder="Enter brand"
                                    value={brand}
                                    onChange={(e) => setBrand(e.target.value)}
                                ></input>
                            </div>
                            <div>
                                <label htmlFor="countInStock">Count In Stock</label>
                                <input
                                    id="countInStock"
                                    type="text"
                                    placeholder="Enter countInStock"
                                    value={countInStock}
                                    onChange={(e) => setCountInStock(e.target.value)}
                                ></input>
                            </div>
                                <div>
                                <label htmlFor="description">Description</label>
                                <textarea
                                    id="description"
                                    rows="3"
                                    type="text"
                                    placeholder="Enter description"
                                    value={description}
                                    onChange={(e) => setDescription(e.target.value)}
                                ></textarea>
                                </div>
                            <div>
                                <label></label>
                                <button className="primary" type="submit">
                                    Update
                                </button>
                            </div>
                        </>
                    }
                </form>
            </div>
        )
    }
    
    export default ProductEditScreen
    

    App.js에 추가한다. 

    import ProductEditScreen from './screens/ProductEditScreen';
    
                  <Route path="/product/:id/edit" component={ProductEditScreen} exact></Route>          
    

     

    productreducer에 다음과 같이 state= {product={}부분을 삭제해준다. 

    export const productDetailsReducer = (state= {loading:true}, action)=>{
        switch(action.type){
            case PRODUCT_DETAILS_REQUEST:
                return {loading:true}
            case PRODUCT_DETAILS_SUCCESS:
                return {loading:false, product:action.payload}
            case PRODUCT_DETAILS_FAIL:
                return {loading:false, error:action.payload}
            default:
                return state;
        }
    }

     

    edit 버튼을 누를때, create Product버튼을 누를때 잘 이동한다. 

     

    여기서 만약 edit버튼을 누르고 나서 create product를 들어가면 전에 등록한 내용이 그대로 남아있는 것을 알 수 있는데, 

    다음과 같이 조건을 추가해서 지금 아이템아이디와 상품아이디가 같지않으면 다시 디테일을 로드하도록 설정하자.

    productEditScreen.js 

            if(!product || product._id!== productId){//만약 상품을 가져오지 못했으면 백엔드에서 불러온다. 
    

     

     

    이제 업데이트 버튼을 활성화 시켜보자. 

     

     

    우선 업데이트 라우터를 만들자. 

    productRouter.put('/:id', isAuth, isAdmin, expressAsyncHandler(async(req, res)=>{
        const productId = req.params.id;
        const product = await Product.findById(productId)
        if(product){
            product.name = req.body.name;
            product.price = req.body.price;
            product.image = req.body.image;
            product.category = req.body.category;
            product.brand = req.body.brand;
            product.countInStock = req.body.countInStock;
            product.description = req.body.description;
            const updatedProduct = await product.save();
            res.send({meassge:'Product Updated', product:updatedProduct})
        }else{
            res.status(404).send({message:'Product Not Found'})
        }
    }))

     

    업데이트 constants를 만든다. 

    productConstants.js

    export const PRODUCT_UPDATE_REQUEST = 'PRODUCT_UPDATE_REQUEST';
    export const PRODUCT_UPDATE_SUCCESS = 'PRODUCT_UPDATE_SUCCESS';
    export const PRODUCT_UPDATE_FAIL = 'PRODUCT_UPDATE_FAIL';
    export const PRODUCT_UPDATE_RESET = 'PRODUCT_UPDATE_RESET';
    

    productActions.js

    export const updateProduct = (product)=>async(dispatch, getState)=>{
        dispatch({
            type: PRODUCT_UPDATE_REQUEST,
            payload:product
        })
        const {userSignin:{userInfo}} = getState();
    
        try{
            const {data} = await Axios.put(`/api/products/${product._id}`,product, {
                headers: {Authorization : `Bearer ${userInfo.token}`}
            } )
            dispatch({
                type:PRODUCT_UPDATE_SUCCESS,
                payload:data
            })
        }catch(error){
            dispatch({type:PRODUCT_UPDATE_FAIL, payload:error.response && error.response.data.message? error.response.data.message:error.message})
        }
    }

    productReducers.js

    export const productUpdateReducer = (state = {}, action)=>{
        switch(action.type){
            case PRODUCT_UPDATE_REQUEST:
                return {loading:true}
            case PRODUCT_UPDATE_SUCCESS:
                return {loading:false, success:true}
            case PRODUCT_UPDATE_FAIL:
                return {loading:false, error:action.payload}
            case PRODUCT_UPDATE_RESET:
                return {};
            default:
                return state;
        }
    }

    store.js

    const reducer = combineReducers({
        productList: productListReducer,
        productDetails : productDetailsReducer,
        cart:cartReducer,
        userSignin: userSigninReducer,
        userRegister: userRegisterReducer,
        orderCreate:orderCreateReducer,
        orderDetails:orderDetailsReducer,
        orderPay: orderPayReducer,
        orderMineList:orderMineListReducer,
        userDetails:userDetailsReducer,
        userUpdateProfile:userUpdateProfileReducer,
        productCreate:productCreateReducer,
        productUpdate:productUpdateReducer
    })

     

    ProductEditScreen.js에서 활용한다. 

    버튼을 누르면 디스패치를 이용해서 라우트를 통해서 프로덕트 업데이트를 하고

    업데이트를 완료한 다음에 다시 history.push로 다른페이지로가기위해서

    useSelector를 이용해서 현재정보를 리덕스에서 가져오고

    successUptdate라면 이미 업데이트가 완료된 상태이므로

    프로덕트 업데이트 리셋을 하고 페이지를 이동하도록 한다. 

    import { detailsProduct, updateProduct } from '../actions/productActions'
    
    import { PRODUCT_UPDATE_RESET } from '../constants/productConstants'
    
    function ProductEditScreen(props) {
    
        const productUpdate = useSelector(state => state.productUpdate)
        const {loading:loadingUpdate, error:errorUpdate, success:successUpdate} = productUpdate
    
        const dispatch = useDispatch()
        useEffect(() => {
            if(successUpdate){
                dispatch({
                    type:PRODUCT_UPDATE_RESET
                })
                props.history.push('/productlist');
            }
            if(!product || product._id!== productId){//만약 상품을 가져오지 못했으면 백엔드에서 불러온다. 
                dispatch(detailsProduct(productId))
            }else{//만약 있으면 
                setName(product.name);
                setPrice(product.price);
                setCategory(product.category);
                setImage(product.image);
                setDescription(product.description);
                setBrand(product.brand);
                setCountInStock(product.countInStock);
            }
        }, [product, dispatch, productId])
    
        const submitHandler= (e)=>{
            e.preventDefault();
            dispatch(updateProduct({_id:productId, name, price, countInStock, category, brand, image, description}));
        }
        
        
        
        return (
            <div>
                <form onSubmit= {submitHandler} className="form">
                    <div>
                        <h1>EditProdcut {productId}</h1>
                    </div>
                    {loadingUpdate && <LoadingBox></LoadingBox>}
                    {errorUpdate && <MessageBox variant='danger'>{errorUpdate}</MessageBox>}
                    {loading? <LoadingBox></LoadingBox> :
                    error? <MessageBox variant='danger'>{error}</MessageBox>:
                        <>

     

     

    업데이트 누르면 잘 수정되고 상품 리스트페이지로 간다. 

     

    그러나 만약 상품을 업데이트하고나서 다시한번더 수정하려고 같은 상품의 edit을 클릭하면 

    수정하기 이전의 스테이트가 보이는 것을 확인할 수 있다. 

    이를 수정해보자. 

        useEffect(() => {
            if(successUpdate){
                props.history.push('/productlist');
            }
            if(!product || product._id!== productId ||successUpdate){//만약 상품을 가져오지 못했으면 백엔드에서 불러온다. 
                dispatch({
                    type:PRODUCT_UPDATE_RESET
                })
                dispatch(detailsProduct(productId))
            }

     

     

    상품 스테잇을 리셋해주는 위치를 바꿨고, successUpdate된 상태에서 다시 에딧을 들어오면 리셋하도록 조건을 바꿨다. 

     

     

    이제 이미지를 직접 선택해서 넣도록 바꿔보자.

     먼저 상품을 업로드하는 uploadRouter.js파일을 라우터 폴더안에 만들어준다. 

     

    우리는 이미지를 업로드하기위해서 multer라는 라이브러리를 사용할 것이다. 

    최상위 디렉토리에서 npm install multer 해준다. 

    설치를 완료하고나서 uploadRouter.js를 완성해준다. 

    import multer from 'multer';
    import express from 'express';
    
    const uploadRouter = express.Router();
    
    const storage= multer.diskStorage({//업로드위해 multer사용
        destination(req, file, cb){//저장할 위치
            cb(null, 'uploads/')
        },
        filename(req, file, cb){//파일이름 지정
            cb(null, `${Date.now()}.jpg`);
        }
    });
    
    const upload = multer({storage});//미들웨어
    
    uploadRouter.post('/', upload, (req, res)=>{
        res.send(`/${req.file.path}`)
    })
    
    export default uploadRouter

    새로운 이미지들을 저장하기 위해 최상위 디렉토리에 uploads폴더를 생성해준다. 

    업로드한 사진들은 이 폴더에 저장이 될 것이다. 

    uploads폴더안에 file.txt라는 파일을 하나 만들어주는데, 

    이는 깃리파지토리에 업로드파일들을 저장하기 위해서 빈파일을 만든 것이다. 

     

     

    새로운 라우터.js를 만들었으니 server.js에 넣어주자. 

    import uploadRouter from './routes/uploadRouter.js';
    app.use('/api/uploads', uploadRouter);
    

     

     

    이제 이미지 인풋박스를 수정해보자. 

    productEditScreen.js

    파일을 가져오고 (axios보내서)

    이미지영역밑에 추가해준다. 

    여기서 보내줄때 파일이름을 image로 append해줬다. 

        const [loadingUpload, setLoadingUpload] = useState(false)
        const [errorUpload, setErrorUpload] = useState('')
        const userSignin = useSelector(state => state.userSignin)
        const {userInfo} = userSignin
        const uploadFileHandler= async(e)=>{
            const file = e.target.files[0]//하나의 파일만 업로드가능
            const bodyFormData = new FormData();
            bodyFormData.append('image', file);
            setLoadingUpload(true);
            try{
                const{data} = await axios.post('/api/uploads', bodyFormData,{
                    headers:{'Content-Type':'multipart/form-data',
                    Authorization:`Bearer ${userInfo.token}`}
                })
                setImage(data)
                setLoadingUpload(false);
            }catch(error){
                setErrorUpload(error.message)
                setLoadingUpload(false)
            }
    
        }
        
        
        
        
        						<div>
                                <label htmlFor="image">Image</label>
                                <input
                                    id="image"
                                    type="text"
                                    placeholder="Enter image"
                                    value={image}
                                    onChange={(e) => setImage(e.target.value)}
                                ></input>
                            </div>
                             <div>
                                <label htmlFor="imageFile">ImageFile</label>
                                <input type="file" id = "imageFile" label="Choose Image" onChange={uploadFileHandler}/>
                            </div>
                            {loadingUpload && <LoadingBox></LoadingBox>}
                            {errorUpload && <MessageBox variant='danger'>{errorUpload}</MessageBox>}
                            <div>

     

    라우터로 돌아가서 인증된 사용자만 사용가능하도록 만들어주고 두번째파라미터를 넣어주자. 

    위에서 파일이름을 image로 append해줬으니 그걸 이용해서 받는다. 

    uploadRouter.post('/', isAuth, upload.single('image'), (req, res)=>{
        res.send(`/${req.file.path}`)
    })

     

    파일 선택이 잘 된다. 

     

    여기서 업데이트를 하면 이런식으로 파일이 깨지는데, 이를 해결해보자. 

     

    server.js에가서 이미지가 uploads폴더에 있다는 것을 알려주는 것이 필요하다. 

    import path from 'path';
    
    
    const __dirname = path.resolve();
    app.use('/uploads', express.static(path.join(__dirnmae, '/uploads')))
    

     

    사진이 정상적으로 잘 뜨는 것을 확인할 수 있다. 

     

    마지막으로 최상위 디렉토리 안의 .gitignore파일안에 /uploads/*.jpg를 저장한다.

    uploads폴더안의 jpg파일들을 깃리파지토리에 저장하지 말라는 의미다. 

    # dependencies
    node_modules
    /.pnp
    .pnp.js
    
    # testing
    /coverage
    
    # production
    build
    
    # misc
    .DS_Store
    .env.local
    .env.development.local
    .env.test.local
    .env.production.local
    
    npm-debug.log*
    yarn-debug.log*
    yarn-error.log* 
    package-lock.json
    
    .env
    /uploads/*.jpg

     

     

    이제 마지막으로 delete버튼을 활성화 시키자. 

     

     

    우선 상품을 지우는api를 생성하자. (백엔드)

    productrouter.js

    productRouter.delete('/:id', isAuth, isAdmin, expressAsyncHandler(async(req, res)=>{
        const product = await Product.findById(req.params.id);
        if(product){
            const deletedProduct = await product.remove();
            res.send({message:'Product Deleted', product:deletedProduct})
        }else{
            res.status(404).send({message:'Product Not Found'})
        }
    }))

    이제 프론트엔드로 가서 axios보내자. 

     

    productListScreen.js

        const deleteHandler = (product)=>{
            dispatch(deleteProduct(product._id));
        }
    

     

     

    productconstants.js

    
    export const PRODUCT_DELETE_REQUEST = 'PRODUCT_DELETE_REQUEST';
    export const PRODUCT_DELETE_SUCCESS = 'PRODUCT_DELETE_SUCCESS';
    export const PRODUCT_DELETE_FAIL = 'PRODUCT_DELETE_FAIL';
    export const PRODUCT_DELETE_RESET = 'PRODUCT_DELETE_RESET';
    

    productActions.js

    export const deleteProduct = (productId) =>async(dispatch, getState)=>{
        dispatch({
            type:PRODUCT_DELETE_REQUEST, payload:productId
        });
        const {userSignin:{userInfo}} = getState();
        try{
            const {data} = await Axios.delete(`/api/products/${productId}`,
            {headers:{Authorization: `Bearer ${userInfo.token}`}});
            dispatch({type:PRODUCT_DELETE_SUCCESS})
    
        }catch(error){
            dispatch({type:PRODUCT_DELETE_FAIL, payload:error.response && error.response.data.message? error.response.data.message:error.message})
        }
    
    }

     

    productReducers.js

    export const productDeleteReducer = (state= {}, action)=>{
        switch(action.type){
            case PRODUCT_DELETE_REQUEST:
                return {loading:true}
            case PRODUCT_DELETE_SUCCESS:
                return {loading:false, success:true}
            case PRODUCT_DELETE_FAIL:
                return {loading:false, error:action.payload}
            case PRODUCT_DELETE_RESET:
                return {};
            default:
                return state;
        }
    }

     

    store.js

    const reducer = combineReducers({
        productList: productListReducer,
        productDetails : productDetailsReducer,
        cart:cartReducer,
        userSignin: userSigninReducer,
        userRegister: userRegisterReducer,
        orderCreate:orderCreateReducer,
        orderDetails:orderDetailsReducer,
        orderPay: orderPayReducer,
        orderMineList:orderMineListReducer,
        userDetails:userDetailsReducer,
        userUpdateProfile:userUpdateProfileReducer,
        productCreate:productCreateReducer,
        productUpdate:productUpdateReducer,
        productDelete:productDeleteReducer
    })

     

     

    productListScreen.js

    import { createProduct, listProducts,deleteProduct } from '../actions/productActions';
    import { PRODUCT_DELETE_RESET } from '../constants/productConstants';
    
        const productDelete = useSelector(state => state.productDelete)
        const {loading:loadingDelete, error:errorDelete, success:successDelete}  = productDelete
        const deleteHandler = (product)=>{
            dispatch(deleteProduct(product._id));
        }
        
        useEffect(() => {
            if(successCreate){
                dispatch({
                    type:PRODUCT_CREATE_RESET
                })
                props.history.push(`/product/${createdProduct._id}/edit`)
            }
            if(successDelete){
                dispatch({type:PRODUCT_DELETE_RESET})
            }
            dispatch(listProducts())
        }, [createdProduct, dispatch, props.history, successCreate, successDelete])
    
    
    
    			{loadingDelete && <LoadingBox></LoadingBox>}
                {errorDelete &&<MessageBox variant='danger'>{errorDelete}</MessageBox>}
                {loadingCreate && <LoadingBox></LoadingBox>}
                {errorCreate &&<MessageBox variant='danger'>{errorCreate}</MessageBox>}

     

     

    다음과 같이 상품을 삭제할 수 있다. 

     

    실수로 상품을 삭제할 수도 있으니, 

    경고창을 띄워서 확실하게 삭제하도록 만들자 

        const deleteHandler = (product)=>{
            if(window.confirm('Are you sure to delete')){
                dispatch(deleteProduct(product._id));
            }
        }

     

     

     

     

     

     

     

     

     

Designed by Tistory.