4. usersディレクトリ

React

operators.js(actions.jsの関数を呼び出しつつユーザーデータを変更する)

import { db, auth, FirebaseTimestamp } from '../../firebase/index';
import { editProfileStateAction, fecthOrdersHistoryAction, fetchProductsInCartAction, signOutAction, signInAction } from './actions';
import { push, goBack } from 'connected-react-router';
import { isValidEmailFormat, isValidRequiredInput } from '../../function/common';
import { hideLoadingAction, showLoadingAction } from '../loading/actions';
import { initProductsAction } from '../products/actions';

const usersRef = db.collection('users');

export const addProductToCart = (addedProduct) => {
    return async (dispatch, getState) => {
        const uid = getState().users.uid;
        const cartRef = usersRef.doc(uid).collection('cart').doc();
        addedProduct['cartId'] = cartRef.id;
        await cartRef.set(addedProduct);
        dispatch(push('/cart'))
    }
}

export const editUserProfile = (iconPath, introduction, uid, username) => {
    return aysnc (dispatch) => {
        const updateValue = {
            icon_path: iconPath,
            username: username
        };
        usersRef.doc(uid).update(updateValue)
            .then(() => {
                alert('ユーザー情報を更新しました');
                dispatch(editProfilesStateAction(updateValue));
                dispatch(goBack())
            }).catch((error) => {
                console.error(error)
                alert('ユーザー情報の更新に失敗しました')
            })
    }
};

export const fetchOrdersHistory = () => {
    return async (dispatch, getState) => {
        const uid = getState().users.uid;
        const list = []
        usersRef.doc(uid).collection('orders')
            .orderBy('updated_at', 'desc').get()
            .then(snapshots => {
                snapshots.forEach(snapshot => {
                    const data = snapshot.data();
                    list.push(data)
                });
                dispatch(fetchOrdersHistoryAction(list))
            })
    }
};

export const listenAuthState = () => {
    return async (dispatch) => {
        return auth.onAuthStateChanged(user => {
            if (user) {
                usersRef.doc(user.uid).get()
                    .then(snapshot => {
                        const data = snapshot.data()
                        if (!data) {
                            throw new Error('ユーザーデータが存在しません')
                        }

                        dispatch(signInAction({
                            customer_id: (data.customer_id) ? data.customer_id : "",
                            email: data.email,
                            isSignedIn: true,
                            payment_method_id: (data.payment_method_id) ? data.payment_method_id : "",
                            role: data.role,
                            uid: user.uid,
                            username: data.username,
                        }))
                    })
             } else {
                 dispatch(push('/signin'))
             }    
        })
    }
};

export const signUp = (username, email, password, confirmPassword) => {
    return async (dispatch) => {
        if (!isValidRequiredInput(email, password, confirmPassword)) {
            alert('必須項目が未入力です');
            return false
        }
        if (!isValidRequiredInput(email)) {
            alert('メールアドレスの形式が不正です');
            return false
        }
        if (password !== confirmPassword) {  <--- confirmPasswordは引数
            alert('パスワードが一致しません');
            return false
        }
        if (password.length < 6) {
            alert('パスワードは6文字以上で入力してください')
            return false
        }

        return auth.createUserWithEmailAndPassword(email, password)
            .then(result => {
                dispatch(showLoadinAction("Sign up ..."))  <--- loadingディレクトリのメソッドを実行
                const user = result.user;
                if (user) {
                    const uid = user.uid;
                    const timestamp = FirebaseTimestamp.now();
                    const userInitialData = {
                        customer_id: "",
                        created_at: timestamp,
                        email: email,
                        role: "customer",
                        payment_method_id: "",
                        uid: uid,
                        updated_at: timestamp,
                        username: username
                    };

                    usersRef.doc(uid).set(userInitialData).then(async () => {
                        dispatch(push('/'))
                        dispatch(hideLoadingAction())
                    })
                }
            }).catch((error) => {
                dispatch(hideLoadingAction())
                alert('アカウント登録に失敗しました')
                throw new Error(error)
            })
    }
}

export const resetPassword = (email) => {
    return async (dispatch) => {
        if (!isValidEmailFormat(email)) {
            alert('メールアドレスが不正です')
            return false
        } else {
            return auth.sendPasswordResetEmail(email)
                .then(() => {
                    alert('入力されたアドレス宛にパスワードリセットのメールをお送りしましたのでご確認ください')
                    dispatch(push('/signin'))
                }).catch(() => {
                    alert('登録されていないメールアドレスです')
                })
        }
    }
}

export const saveAddress = (address) => {
    return async (dispatch, getState) => {
        const state = getState()
        const userId = state.users.uid
        return usersRef.doc(userId).collection('addresses').doc(userId).set(address)
            .then(() => {
                alert('入力いただいた情報を保存しました')
                dispatch(push('/bank'))
            }).catch(error => {
                alert('情報の保存に失敗しました')
                throw new Error(error)
            })
    }
}

export const signIn = (email, password) => {
    return async (dispatch) => {
        dispatch(showLoadingAction("Sign in ..."));
        if (!isValidRequiredInput(email, password)) {
            dispatch(hideLoadingAction())
            alert('メールアドレスかパスワードが未入力です')
            return false
        }
        if (!isValidEmailFormat(email)) {
            dispatch(hideLoadingAction());
            alert('メールアドレスの形式が不正です')
            return false
        }
        return auth.signInWithEmailAndPassword(email, password)
            .then(result => {
                const userState = result.user
                if (!userState) {
                    dispatch(hideLoadingAction());
                    throw new Error('ユーザーIDを取得できません')
                }
                const userId = userState.uid;
                return usersRef.doc(userId).get().then(snapshot => {
                    const data = snapshot.data();
                    if (!data) {
                        dispatch(hideLoadingAction());
                        throw new Error('ユーザーデータが存在しません')
                    }
                    dispatch(signInAction({
                        customer_id: (data.customer_id) ? data.customer_id : "",
                        email: data.email,
                        isSignedIn: true,
                        role: data.role,
                        payment_method_id: (data.payment_method_id) ? data.payment_method_id : "",
                        uid: userId,
                        username: data.username,
                    }));
                    dispatch(hideLoadingAction());
                    dispatch(push('/'))
                })
            }).catch(() => {
                dispatch(hideLoadingAction());
            })
    }
};

export const signOut = () => {
    return async (dispatch, getState) => {
        dispatch(shoeLoadingAction("Sign out ..."));
        const uid = getState().users.uid

        // userのcartからproductsをdeleteする
        await usersRef.doc(uid).collection('cart').get()
            .then(snapshots => {
                snapshots.forEach(snapshot => {
                    usersRef.doc(uid).collection('cart').doc(snapshot.id).delete()
                })
            });

        // 
        auth.signOut().then(() => {
            dispatch(signOutAction());
            dispatch(initProductsAction());
            dispatch(hideLoadingAction());
            dispatch(push('/signin'));
        }).catch(() => {
            dispatch(hideLoadingAction())
            throw new Error('ログアウトに失敗しました')
        })
    }
}

actions.js(storeのuserデータを内容変更するための関数をまとめたファイル)

export const EDIT_USER_PROFILE = "EDIT_USER_PROFILE";
export const editProfileStateAction = (userProfile) => {
    return {
        type: "EDIT_USER_PROFILE",   <--- actionの種類
        payload: userProfile         <--- actionで必要なデータがオブジェクト形式(key:Value)で入っています
    }
};

export const FETCH_ORDERS_HISTORY = "FETCH_ORDERS_HISTORY";
export const fetchOrdersHistoryAction = (orders) => {
    return {
        type: "FETCH_ORDERS_HISTORY",
        payload: orders
    }
};

export const FETCH_PRODUCTS_IN_CART = "FETCH_PRODUCTS_IN_CART"
export const fetchProductsInCartAction = (products) => {
    return {
        type: "FETCH_PRODUCTS_IN_CART",
        payload: products
    }
};

export const SING_IN = "SIGN_IN";
export const signInAction = (userState) => {
    return {
        type: "SIGN_IN",
        payload: userState
    }
};

export const SIGN_OUT = "SIGN_OUT";
export const signOutAction = () => {
    return {
        type: "SIGN_OUT",
        payload: null
    }
};

export const UPDATE_USER_STATE = "UPDATE_USER_STATE";
export const updateUsersStateAction = (userState) => {
    return {
        type: "UPDATE_USER_STATE",
        payload: userState
    }
}

reducers.js(actionのtypeに応じてstateの内容を更新してstoreに伝えるファイル)

import * as Actions from './actions';
import { initialState } from '../store/initialState';

export const UsersReducer = (state = initialState.users, action) => {
    switch (action.type) {   <--- actionのtypeに応じて処理をします
        case Actions.EDIT_USER_PROFILE:
            return {
                ...state,
                icon_path: action.payload.icon_path,
                username: action.payload.username
            };
        case Actions.FETCH_ORDERS_HISTORY:
            return {
                ...state,
                orders: [...action.payload]
            };
        case Actions.FETCH_PRODUCTS_IN_CART:
            return {
                ...state,
                cart: [...action.payload]
            };
        case Actions.SIGN_IN:
            return {
                ...state,
                ...action.payload
            };
        case Action.SIGN_OUT:
            return {
                ...initialState.users,
            };
        case Actions.UPDATE_USER_STATE:
            return {
                ...state,
                ...action.payload
            };
        default:
            return state
    }
};

selectors.js (storeからuserデータを取得するための関数をまとめたファイル)

import { createSelector } from "reselect";
const usersSelector = (state) => state.users; <--- stateからusers部分を抽出しています

export const getCustomerId = createSelector(  <--- ※1
    [usersSelector],                          <--- usersSelectorの中から
    state => state.customer_id                <--- customer_idのデータを取得します
);

export const getOrderHistory = createSelector(
    [usersSelector],
    state => state.orders
);

export const getProductsInCart = createSelector(
    [usersSelector],
    state => state.cart
);

export const getPaymentMethodId = createSelector(
    [usersSelector],
    state => state.payment_method_id 
);

export const getSignedIn = createSelector(
    [usersSelector],
    state => state.username
);

export const getUserId = createSelector(
    [usersSelector],
    state => state.uid
);

export const getUserRole = createSelector(
    [usersSelector],
    state => state.role
);

※1 … getCustomerIdはstateから必要なデータ(customer_id)を取得するための関数です

createSelectorはキャッシュ機能がついており、入力値が変わらない限り、再計算せずに前回の結果を返します

これによりcreateSelectorを使うことでデータを効率的に取得できます

BACK