ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Social Media 만들기 - 7) userInfoProfile , edit profile 기능 만들기
    NODE.JS 2021. 7. 7. 16:31

    search.js 의 form 형식을 search버튼을 누르면 작동하도록 이전의 useEffect를 함수로 만들자. 

    import React, { useEffect, useState } from 'react'
    import { useDispatch, useSelector } from 'react-redux'
    import { getDataAPI } from '../../utils/fetchData'
    import { GLOBALTYPES } from '../../redux/actions/globalTypes'
    import {Link} from 'react-router-dom'
    import UserCard from '../UserCard'
    
    const Search = () => {
        const [search, setSearch] = useState('')
        const [users, setUsers] = useState([])
    
        const {auth} = useSelector(state => state)
        const dispatch = useDispatch()
    
        const handleSearch= async (e) =>{
            e.preventDefault()
            if(!search) return;
    
            try{
                const res = await getDataAPI(`search?username=${search}`, auth.token)
                setUsers(res.data.users)
    
    
            }catch(err){
                dispatch({
                    type:GLOBALTYPES.ALERT,
                    payload:{error:err.response.data.msg}
                })
            }
    
        }
    
        const handleClose= () =>{
            setSearch('')
            setUsers([])
        }
    
    
    
        return (
            <form className="search_form" onSubmit={handleSearch}>
                <input type="text" name = "search" value={search} id="search"
                onChange={e=>setSearch(e.target.value.toLowerCase().replace(/ /g, ''))} />
                <div className="search_icon" style={{opacity: search? 0 : 0.3}}>
                    <span className="material-icons">search</span>
                    <span>Search</span>
                </div>
                <div className="close_search" style={{opacity: users.length===0? 0:1}}
                    onClick = {handleClose}>
                    &times;
                </div>
    
                <button type="submit">Search</button>
    
                <div className="users">
                    {
                        search && users.map(user=>(
                            <Link key={user._id} to ={`/profile/${user._id}`} onClick={handleClose}>
                                <UserCard user={user} border = "border"/>
                            </Link>
                        ))
                    }
                </div>
            </form>
        )
    }
    
    export default Search

     

    display속성을 none으로 만들어서 엔터를치면 나오도록 하자. 

                <button type="submit" style={{display:'none'}}>Search</button>

     

    loading 이미지를 넣어보자. 

    import LoadIcon from '../../images/loading.gif'
    
    const Search = () => {
    
        const [load, setLoad] = useState(false)
    
        const handleSearch= async (e) =>{
            e.preventDefault()
            if(!search) return;
    
            try{
                setLoad(true)
                const res = await getDataAPI(`search?username=${search}`, auth.token)
                setUsers(res.data.users)
                setLoad(false)
    
        return (
           
                <button type="submit" style={{display:'none'}}>Search</button>
    
                {
                    load && <img src={LoadIcon} alt="loading" />
                }

    스타일링을 주자. 

    .header .search_form .loading{
        position: absolute;
        top: 50%;
        right: 5px;
        width: 15px;
        height: 15px;
        transform: translateY(-50%) ;
    }

     

    Search.js로 가서 함수와 데이터를 prop으로 넘겨줘서 link to를 userCard에서 하도록 하자. 

                <div className="users">
                    {
                        search && users.map(user => (
                            <UserCard 
                            key={user._id} 
                            user={user} 
                            border="border"
                            handleClose={handleClose} 
                            />
                        ))
                    }
                </div>
            </form>
        )
    }
    
    export default Search

     

    userCard.js

    import React from 'react'
    import Avatar from './Avatar'
    import {Link} from 'react-router-dom'
    
    const UserCard = ({user, border, handleClose}) => {
        const handleCloseAll=()=>{
            if(handleClose) handleClose()
        }
        return (
            <div className={`d-flex p-2 align-item-center ${border}`}>
                <div>
                    <Link  to ={`/profile/${user._id}`} onClick={handleCloseAll} className="d-flex align-item-center">
                        <Avatar src={user.avatar} size="big-avatar"/>
                        <div className="ml-1" style={{transform:'translateY(-2px)'}}>
                            <span className="d-block">{user.username}</span>
                            <small style={{opacity:0.7}}>{user.fullname}</small>
                        </div>
                    </Link>
                </div>
            </div>
        )
    }
    
    export default UserCard

     

    userctrl가서 get user하는 라우터를 작성한다. 

        getUser: async (req, res) => {
            try {
                const users = await Users.findById(req.param.id).select('-password')
                if(!user) return res.status(400).json({msg:"User does not exist."})
                res.json({user})
            } catch (err) {
                return res.status(500).json({msg: err.message})
            }
        },

     

    userRouter.js에 추가해준다. 

    const router = require('express').Router()
    const auth = require("../middleware/auth")
    const userCtrl = require("../controllers/userCtrl")
    
    
    router.get('/search', auth, userCtrl.searchUser)
    router.get('/user/:id', auth, userCtrl.getUser)
    
    module.exports = router

     

    profileAction.js를 만들고 추가해준다. 

    import { GLOBALTYPES } from "./globalTypes";
    
    export const PROFILE_TYPES={
        LOADING:'LOADING',
        GET_USER:'GET_USER'
    }

     

    reducer를 만들어준다. 

    import { PROFILE_TYPES } from "../actions/profileAction";
    
    const initialState = {
        loading:false,
        users:[],
        posts:[]
    }
    
    const profileReducer = (state=initialState, action)=>{
        switch(action.type){
            case PROFILE_TYPES.LOADING:
                return {
                    ...state,
                    loading: action.payload
                };
            case PROFILE_TYPES.GET_USER:
                return {
                    ...state,
                    users: [...state.users, action.payload.user]
                };
            default:
                return state;
    
        }
    }
    
    export default profileReducer

     

    index.js에 reducer 추가해준다. 

    import { combineReducers } from "redux";
    import auth from './authReducers';
    import alert from './alertReducer';
    import theme from './themeReducer';
    import profile from './profileReducer'
    
    export default combineReducers({
        auth,
        alert,
        theme,
        profile
    })

     

     

    profile>[id].js로가서 다음과 같이 component를 넣어준다. 

    import React from 'react'
    import Info from '../../components/profile/Info'
    import Post from '../../components/profile/Post'
    
    const profile = () => {
        return (
            <div className="profile">
                <Info />
                <Post />
            </div>
        )
    }
    
    export default profile

     

    component>profile> Info.js, Post.js를 만들어준다. 

    Info.js

    import React, { useEffect, useState } from 'react'
    import { useDispatch, useSelector } from 'react-redux'
    import { useParams, Link } from 'react-router-dom'
    import Avatar from '../Avatar'
    
    
    const Info = () => {
    
        const {id} = useParams()
    
        const {auth} = useSelector(state => state)
        const dispatch = useDispatch()
    
        const [userData, setUserData] = useState([])
    
        useEffect(() => {
            if(id===auth.user._id){
                setUserData([auth.user])
            }
        }, [id,auth.user])
    
        return (
            <div className="info">
            {
                userData.map(user => (
                    <div className="info_container" key={user._id}>
                        <Avatar src={user.avatar} size="supper-avatar" />
                        <div className="info_content">
                            <div className="info_content_title">
                                <h2>{user.username}</h2>
                                <button className="btn btn-outline-info">Edit Profile</button>
                            </div>
                            <div>
                                <span>
                                    {user.followers.length} Followers
                                </span>
                                <span>
                                    {user.following.length} Following
                                </span>
                            </div>
    
                            <h6>{user.fullname}</h6>
                            <p>{user.address}</p>
                            <h6>{user.email}</h6>
                            <a href={user.website} target="_blank"  rel="noreferrer">{user.website}</a>
                            <p>{user.story}</p>
                        </div>
                    </div>
                ))
            }
            </div>
        )
    }
    
    export default Info

    Info.js

    import React, { useEffect, useState } from 'react'
    import { useDispatch, useSelector } from 'react-redux'
    import { useParams, Link } from 'react-router-dom'
    import Avatar from '../Avatar'
    
    
    const Info = () => {
    
        const {id} = useParams()
    
        const {auth} = useSelector(state => state)
        const dispatch = useDispatch()
    
        const [userData, setUserData] = useState([])
    
        useEffect(() => {
            if(id===auth.user._id){
                setUserData([auth.user])
            }
        }, [id,auth.user])
    
        return (
            <div className="info">
            {
                userData.map(user => (
                    <div className="info_container" key={user._id}>
                        <Avatar src={user.avatar} size="supper-avatar" />
                        <div className="info_content">
                            <div className="info_content_title">
                                <h2>{user.username}</h2>
                                <button className="btn btn-outline-info">Edit Profile</button>
                            </div>
                            <div className="follow_btn">
                                <span className="mr-4">
                                    {user.followers.length} Followers
                                </span>
                                <span className="ml-4">
                                    {user.following.length} Following
                                </span>
                            </div>
    
                            <h6>{user.fullname} {user.mobile}</h6>
                            <p className="m-0">{user.address}</p>
                            <h6>{user.email}</h6>
                            <a href={user.website} target="_blank"  rel="noreferrer">{user.website}</a>
                            <p>{user.story}</p>
                        </div>
                    </div>
                ))
            }
            </div>
        )
    }
    
    export default Info

     

    profile.css를 추가해준다. 

      /* ------PROFILE--------- */
      @import url("./profile.css");
    /* Info */
    
    .info{
        width: 100%;
        max-width: 800px;
        margin: auto;
        padding: 20px 10px;
    
    }
    
    .info_container{
        display: flex;
        justify-content: space-around;
        flex-wrap: wrap;
    }
    .info_content{
        min-width: 250px;
        max-width: 400px;
        width: 100%;
        flex: 1;
        opacity: 0.7;
        margin: 0 15px;
    }
    
    .info_content_title{
        display: flex;
        align-items: center;
        flex-wrap: wrap;
    }
    
    .info_content_title h2{
        font-size: 2rem;
        font-weight: 400;
        transform: translateY(4px);
        flex: 3;
    }
    
    .info_content_title button{
        flex: 2;
    }
    
    .info_container .follow_btn{
        color:teal;
        cursor: pointer;
    }
    
    .info_container .follow_btn span:hover{
        text-decoration: underline;
    }

     

    지금은 로그인한 프로필과 같을 때만 가져오는데, 

    다른 유저의 정보들도 가져오도록 dispatch해주자. 

     

     

     

     


    유저의 정보를 가져오도록 profile.js를 만들어준다. 

    import React, { useEffect } from 'react'
    import { useDispatch, useSelector } from 'react-redux';
    import Info from '../component/Info'
    import { getProfileUser } from '../_actions/profileActions';
    import Avatar from '../component/Avatar'
    import { PROFILE_GETUSER_RESET } from '../_constants/profileConstants';
    import Loading from '../component/Loading';
    import Alert from '../component/Alert'
    
    
    function Profile(props) {
        const userId = props.match.params.id;
        
    
        const userLogin = useSelector(state => state.userLogin)
        const {userInfo} = userLogin
        const dispatch = useDispatch()
    
        const userProfile = useSelector(state => state.userProfile)
        const {loading, error, user} = userProfile
    
    
        useEffect(() => {
            if(!user || userId !== user._id){
                dispatch(getProfileUser(userId))
            }
        }, [dispatch, userId, user])
    
        return (
            <div className="profile">
                {loading&& <Loading></Loading>}
                {error && <Alert variant="danger">{error}</Alert>}
                <div className="info">
                    <div className="info_container" key={user?._id}>
                        <Avatar src={user?.avatar} size="supper-avatar" />
                        <div classNamåe="info_content">
                            <div className="info_content_title">
                                <h2>{user?.username}</h2>
                                {
                                    user?._id === userInfo.user._id 
                                    ? <button className="btn btn-outline-info">Edit Profile</button>
                                    :   ''
                                }
                            </div>
                            <div>
                                <span>
                                    {user?.followers.length} Followers
                                </span>
                                <span>
                                    {user?.following.length} Following
                                </span>
                            </div>
    
                            <h6>{user?.fullname}</h6>
                            <p>{user?.address}</p>
                            <h6>{user?.email}</h6>
                            <a href={user?.website} target="_blank"  rel="noreferrer">{user?.website}</a>
                            <p>{user?.story}</p>
                        </div>
                    </div>
                </div>
            </div>
        )
    }
    
    export default Profile

     

    userRouter에 유저정보 가져오는 라우터 추가해준다. 

    userRouter.get('/:id',auth,  async(req, res)=>{
        try{
            const user = await User.findById(req.params.id)
                                    .select('-password')
            if(user){
                res.send(user)
            }else{
                res.status(404).send({message:'User Not Found.'})
            }
        }catch(err){
            return res.status(500).json({message: err.message})
        }
    })

     

    profile constant

    export const PROFILE_GETUSER_REQUEST = 'PROFILE_GETUSER_REQUEST'
    export const PROFILE_GETUSER_SUCCESS = 'PROFILE_GETUSER_SUCCESS'
    export const PROFILE_GETUSER_FAIL = 'PROFILE_GETUSER_FAIL'
    export const PROFILE_GETUSER_RESET = 'PROFILE_GETUSER_RESET'

     

    profileActions.js

    import axios from "axios"
    import { PROFILE_GETUSER_FAIL, PROFILE_GETUSER_REQUEST, PROFILE_GETUSER_SUCCESS } from "../_constants/profileConstants"
    
    export const getProfileUser = (userId)=>async (dispatch, getState)=>{
        dispatch({
            type:PROFILE_GETUSER_REQUEST,
            payload:userId
        })
        const {userLogin:{userInfo}} = getState()
        try{
            const {data} = await axios.get(`/api/users/${userId}`,{headers:{authorization : `Bearer ${userInfo?.token}`}
            })
    
            dispatch({
                type:PROFILE_GETUSER_SUCCESS,
                payload:data
            })
    
    
        }catch (err){
            dispatch({
                type:PROFILE_GETUSER_FAIL,
                payload:{error:err.response && err.response.data.message? err.response.data.message : err.message}
            })
        }
    }

     

    profile Reducer.js

    import { PROFILE_GETUSER_FAIL, PROFILE_GETUSER_REQUEST, PROFILE_GETUSER_RESET, PROFILE_GETUSER_SUCCESS } from "../_constants/profileConstants";
    
    export const getUserProfileReducer = (state={loading:true}, action)=>{
        switch(action.type){
            case PROFILE_GETUSER_REQUEST:
                return {loading:true};
            case PROFILE_GETUSER_SUCCESS:
                return {loading:false, user:action.payload}
            case PROFILE_GETUSER_FAIL:
                return {loading:false, error:action.payload}
            case PROFILE_GETUSER_RESET:
                return {loading:true}
            default:
                return state;
        }
    }

     

    store.js

    const reducer = combineReducers({
        userLogin:userLoginReducer,
        userRegister:userRegisterReducer,
        alert:alertReducer,
        theme:themeReducer,
        userProfile:getUserProfileReducer,
    
        
    
    })

     

    profile.css

    .profile{
        width: 100%;
        min-height: 100vh;
    }
    /* ------ Info ---------- */
    .info{
        width: 100%;
        max-width: 800px;
        margin: auto;
        padding: 20px 10px;
    }
    .info_container{
        display: flex;
        justify-content: space-around;
        flex-wrap: wrap;
    }
    
    .info_content{
        min-width: 250px;
        max-width: 550px;
        width: 100%;
        flex: 1;
        opacity: 0.7;
        margin: 0 15px;
    }
    .info_content_title{
        display: flex;
        align-items: center;
        flex-wrap: wrap;
    }
    .info_content_title h2{
        font-size: 2rem;
        font-weight: 400;
        transform: translateY(4px);
        flex: 3;
    }
    .info_content_title button{
        flex: 2;
    }
    .info_container .follow_btn{
        color: teal;
        cursor: pointer;
    }
    .info_container .follow_btn span:hover{
        text-decoration: underline;
    }
    
    /* -------------- Profile Edit ------------ */
    .edit_profile{
        position: fixed;
        top:0;
        left: 0;
        width: 100%;
        height: 100vh;
        background: #0008;
        z-index: 9;
        overflow: auto;
    }
    .edit_profile form{
        max-width: 450px;
        width: 100%;
        background: white;
        padding: 20px;
        border-radius: 5px;
        margin: 20px auto;
    }
    .edit_profile .btn_close{
        position: absolute;
        top: 1rem;
        right: 1rem;
    }
    .edit_profile .info_avatar{
        width: 150px;
        height: 150px;
        overflow: hidden;
        border-radius: 50%;
        position: relative;
        margin: 15px auto;
        border: 1px solid #ddd;
        cursor: pointer;
    }
    .edit_profile .info_avatar img{
        width: 100%;
        height: 100%;
        display: block;
        object-fit: cover;
    }
    .edit_profile .info_avatar span{
        position: absolute;
        bottom: -100%;
        left: 0;
        width: 100%;
        height: 50%;
        text-align: center;
        color: orange;
        transition: 0.3s ease-in-out;
        background: #fff5;
    }
    .edit_profile .info_avatar:hover span{
        bottom: -15%;
    }
    .edit_profile .info_avatar #file_up{
        position: absolute;
        top:0;
        left: 0;
        width: 100%;
        height: 100%;
        cursor: pointer;
        opacity: 0;
    }
    ::-webkit-file-upload-button{
        cursor: pointer;
    }
    
    /* ----------- Follow ------- */
    .follow{
        position: fixed;
        top:0;
        left: 0;
        background: #0008;
        width: 100%;
        height: 100vh;
        z-index: 4;
    }
    .follow_box{
        width: 350px;
        border: 1px solid #222;
        border-radius: 5px;
        background: white;
        padding: 20px 10px;
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
    }
    .follow_content{
        width: 100%;
        height: 350px;
        overflow: auto;
    }
    .follow_box .close{
        position: absolute;
        top: 0;
        right: 3px;
        font-size: 3rem;
        cursor: pointer;
    }
    
    /* --------- Save Tab ----------- */
    .profile_tab{
        width: 100%;
        display: flex;
        justify-content: center;
        border-top: 1px solid #eee;
        border-bottom: 1px solid #eee;
    }
    .profile_tab button{
        outline: none;
        border: none;
        background: white;
        text-transform: uppercase;
        font-weight: bold;
        font-size: 1.2rem;
        padding: 5px 20px;
        margin: 0 25px;
        opacity: 0.5;
    }
    .profile_tab button.active{
        border-top: 1px solid #000;
        border-bottom: 1px solid #000;
        opacity: 0.8;
    }

     

     

    global.css에 추가해준다. 

    /* -------- Profile ---------- */
    @import url("./profile.css");

     

    이제 edit profile 버튼을 활성화 시키자. 

    profile.js에 editprofile.js컴포넌트를 넣어주는데, x 표시를 활성화 하기 위해서 setOnEdit을 prop으로 넘겨준다. 

                                <span>
                                    {user?.followers.length} Followers
                                </span>
                                <span>
                                    {user?.following.length} Following
                                </span>
                            </div>
    
                            {
                                onEdit && <EditProfile setOnEdit = {setOnEdit}/>
                            }

     

    editprofile.js

    여기서 아바타는 theme가 변경되어도 변화 없게끔 설정해줬다. 

    파일 업로더를 이용해서 설정했다. 

     

    import React from 'react'
    import { useState } from 'react'
    import { useSelector } from 'react-redux'
    import Alert from '../component/Alert'
    
    function EditProfile({setOnEdit}) {
    
        const userLogin = useSelector(state => state.userLogin)
        const {userInfo} = userLogin
        const theme = useSelector(state => state.theme)
    
        const [fullname, setFullname] = useState(userInfo.user.fullname)
        const [mobile, setMobile] = useState(userInfo.user.mobile)
        const [address, setAddress] = useState(userInfo.user.address)
        const [website, setWebsite] = useState(userInfo.user.website)
        const [story, setStory] = useState(userInfo.user.story)
        const [gender, setGender] = useState(userInfo.user.gender)
        const [avatar, setAvatar] = useState('')
    
    
        const changeAvatar=(e) =>{
            const file = e.target.files[0]
            const err = checkImage(file)
            if(err)return window.alert(err)
    
            setAvatar(file)
        }
        
        const checkImage = (file)=>{
            let err = ""
            if(!file) return err = "File does not exist."
            if(file.size> 1024*1024){
                err = "The alrgest image size is 1mb."
            }
            if(file.type !=='image/jpeg' && file.type !== 'image/png'){
                err = "Image format is incorrect."
            }
            return err
        }
        const handleSubmit = (e) =>{
            e.preventDefault()
    
        }
        return (
            <div className="edit_profile">
                <button className="btn btn-danger btn_close" onClick={()=>setOnEdit(false)}>
                    Close
                </button>
    
                <form onSubmit={handleSubmit}>
                    <div className="info_avatar">
                        <img src={avatar? URL.createObjectURL(avatar):userInfo.user.avatar} alt="avatar" style={{filter:theme? 'invert(1)':'inver(0)'}}/>
                        <span>
                            <i className="fas fa-camera"></i>
                            <p>Change</p>
                            <input type="file" name="file" id="file_up" accept="image/*" onChange={changeAvatar}/>
                        </span>
                        
                    </div>
    
    
                    <div className="form-group">
                        <label htmlFor="fullname">Full Name</label>
                        <div className="position-relative">
                            <input type="text" className="form-control" id="fullname" name="fullname"
                                onChange={e=>setFullname(e.target.value)} value={fullname} required/>
                            <small className="text-danger position-absolute" style={{top:'50%', right:'5px', transform:'translateY(-50%)'}}>
                                {fullname.length}/25
                            </small>
                        </div>
                    </div>
    
                    <div className="form-group">
                        <label htmlFor="mobile">Mobile</label>
                        <input type="text" className="form-control" id="mobile" name="mobile"
                            onChange={e=>setMobile(e.target.value)} value={mobile}/>
                    </div>
    
                    <div className="form-group">
                        <label htmlFor="address">Address</label>
                        <input type="address" className="form-control" id="address" name="address"
                            onChange={e=>setAddress(e.target.value)} value={address} />
                    </div>
    
                    <div className="form-group">
                        <label htmlFor="website">Website</label>
                        <input type="text" className="form-control" id="website" name="website"
                            onChange={e=>setWebsite(e.target.value)} value={website} />
                    </div>
    
                    <div className="form-group">
                        <label htmlFor="story">Story</label>
                        <textarea name="story" id="story" cols="30" rows="4" value={story} onChange={(e)=>setStory(e.target.value)}></textarea>
                        <small className="text-danger d-block text-right" >
                                {story.length}/200
                        </small>
                    </div>
    
                    <label htmlFor="gender">Gender</label>
                    <div className="input-group-prepend px-0 mb-4">
                        <select className="custom-select text-capitalize" name="gender" id="gender" value={gender} onChange={e=>setGender(e.target.value)}>
                            <option value="male">Male</option>
                            <option value="female">Female</option>
                            <option value="other">Other</option>
                        </select>
                        
                    </div>
    
                    <button className="btn btn-info w-100" type="submit">
                        Save
                    </button>
                </form>
            </div>
        )
    }
    
    export default EditProfile

    사진 너무 크거나 오류나는 사진 올리면, 

    오류메세지 뜨면서 업로드 안된다. 

     

     

    edit profile router를 작성해주자. 

    userRouter.put('/:id', auth, async(req, res)=>{
        try{
            const user = await User.findById(req.params.id)
            if(user){
                user.fullname = req.body.fullname || user.fullname
                user.avatar = req.body.avatar || user.avatar
                user.mobile = req.body.mobile || user.mobile
                user.address = req.body.address || user.address
                user.website = req.body.website || user.website
                user.story = req.body.story || user.story
                user.gender = req.body.gender || user.gender
            }
            const updatedUser = await user.save()
            res.send({
                user:updatedUser,
                token:generateToken(updatedUser)
            })
        }catch(err){
            return res.status(500).json({message:err.message})
        }
    })

     

    edit profile action

    export const updateUserProfile = (user ) =>async (dispatch, getState)=>{
        dispatch({
            type:USER_UPDATE_PROFILE_REQUEST,
            payload:{user}
        })
        const {userLogin: {userInfo}} = getState()
        try{
    
            const {data} = await axios.put(`/api/users/${userInfo.user._id}`, user, {
                headers: {authorization : `Bearer ${userInfo.token}`}
            })
            dispatch({
                type:USER_UPDATE_PROFILE_SUCCESS,
                payload : data
            })
            // dispatch({
            //     type:USER_LOGIN_SUCCESS,
            //     payload:{
            //         token:userInfo.token,
            //         user:{data}
            //     }
            // })
            dispatch({
                type:USER_LOGIN_SUCCESS,
                payload:data
            })
            localStorage.removeItem('userInfo')
            localStorage.setItem('userInfo',JSON.stringify(data))
    
        }catch(err){
            dispatch({
                type:USER_UPDATE_PROFILE_FAIL,
                payload:{error:err.response && err.response.data.message? err.response.data.message : err.message}
            })
        }
    }

    edit profile reducer

    export const userUpdateProfileReducer = (state={}, action)=>{
        switch(action.type){
            case USER_UPDATE_PROFILE_REQUEST:
                return {loading:true}
            case USER_UPDATE_PROFILE_SUCCESS:
                return {loading:false, success:true}
            case USER_UPDATE_PROFILE_FAIL:
                return {loading:false, error:action.payload}
            case USER_UPDATE_PROFILE_RESET:
                return {}
            default:
                return state
        }
    }

     

     

     

     

    store.js

    const reducer = combineReducers({
        userLogin:userLoginReducer,
        userRegister:userRegisterReducer,
        alert:alertReducer,
        theme:themeReducer,
        userProfile:getUserProfileReducer,
        userUpdateProfile:userUpdateProfileReducer,
    
        
    
    })

     

     

     

     

     

     

     

     

     

     

     

     

     

Designed by Tistory.