OVERVIEW

Untitled

테마 메뉴 개발

src/components/Menu/ThemeMenu.jsx

firebase : onChildAdded(쿼리, 콜백, 취소콜백)

특정 위치에서 데이터 변경 사항을 수신합니다. 이것은 데이터베이스에서 데이터를 읽는 기본 방법입니다. 콜백은 초기 데이터에 대해 그리고 데이터가 변경될 때마다 다시 트리거됩니다. 반환된 구독 취소 콜백을 호출하여 업데이트 수신을 중지합니다. 자세한 내용 은 웹에서 데이터 검색 을 참조하십시오. onChildAdded 이벤트는 이 위치의 각 초기 자식에 대해 한 번 트리거되고 새 자식이 추가될 때마다 다시 트리거됩니다. 콜백에 전달된 DataSnapshot 은 관련 자식에 대한 데이터를 반영합니다. 정렬을 위해 정렬 순서에 따라 이전 형제 자식의 키를 포함하는 문자열인 두 번째 인수가 전달되거나 첫 번째 자식인 경우 null 이 전달됩니다.

import {
  Button,
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  List,
  ListItem,
  ListItemIcon,
  Stack,
} from '@mui/material'
import React, { useCallback, useEffect, useState } from 'react'
import PaletteIcon from '@mui/icons-material/Palette'
import { HexColorPicker } from 'react-colorful'
import '../../firebase'
import {
  child,
  getDatabase,
  onChildAdded,
  push,
  ref,
  update,
} from 'firebase/database'
import { useDispatch, useSelector } from 'react-redux'
import { setTheme } from '../../store/themeReducer'

export default function ThemeMenu() {
  // 유저 정보 가져오기
  const { user } = useSelector(state => state)

  // dispatch
  const dispatch = useDispatch()

  // 테마 모달 
  const [showThemeModal, setShowThemeModal] = useState(false)

  // 메인 테마 색상
  const [mainTheme, setMainTheme] = useState('#fffff')

  // 서브 테마 색상
  const [subTheme, setSubTheme] = useState('#fffff')

  // 유저 테마 배열
  const [userTheme, setUserTheme] = useState([])

  // 모달 열기
  const handleClickOpen = useCallback(() => {
    setShowThemeModal(true)
  }, [])

  // 모달 닫기
  const handleClickClose = useCallback(() => {
    setShowThemeModal(false)
  }, [])

  // 메인 테마 바꾸기
  const handleChangeMain = useCallback(color => {
    setMainTheme(color)
  }, [])

  // 서브 테마 바꾸기
  const handleChangeSub = useCallback(color => {
    setSubTheme(color)
  }, [])

  // 테마 저장하기
  const handleSaveTheme = useCallback(async () => {
    // 유저 정보 없을 경우 방어
    if (!user.currentUser?.uid) return
    try {
      const db = getDatabase() // 데이터베이스 가져오기
      // 키 생성
      const key = push(
        child(ref(db), '/users/' + user.currentUser.uid + '/theme')
      ).key
      // 새로운테마 생성
      const newTheme = { mainTheme, subTheme }
      // firebase 데이터에 업데이트하기
      const updates = {}
      updates['/users/' + user.currentUser.uid + '/theme/' + key] = newTheme
      await update(ref(db), updates)
      handleClickClose()
    } catch (error) {
      console.error(error)
      handleClickClose()
    }
  }, [mainTheme, subTheme, user.currentUser?.uid, handleClickClose])

  // 생성한 전체 테마 배열 만들기
  useEffect(() => {
    if (!user.currentUser?.uid) return
    const db = getDatabase()
    const themeRef = ref(db, 'users/' + user.currentUser.uid + '/theme')
    const unsubscribe = onChildAdded(themeRef, snap => {
      setUserTheme(themeArr => [snap.val(), ...themeArr])
    })
    return () => {
      setUserTheme([])
      unsubscribe?.()
    }
  }, [user.currentUser.uid])

  return (
    <>
      <List sx={{ overflow: 'auto', width: 60, backgroundColor: '#150c16' }}>
        <ListItem button onClick={handleClickOpen}>
          <ListItemIcon sx={{ color: 'white' }}>
            <PaletteIcon />
          </ListItemIcon>
        </ListItem>

        {userTheme.map(theme => (
          <ListItem>
            <div
              className="theme-box"
              onClick={() =>
                dispatch(setTheme(theme.mainTheme, theme.subTheme))
              }
            >
              <div
                className="theme-main"
                style={{ backgroundColor: theme.mainTheme }}
              ></div>
              <div
                className="theme-sub"
                style={{ backgroundColor: theme.subTheme }}
              ></div>
            </div>
          </ListItem>
        ))}
      </List>
      <Dialog open={showThemeModal} onClose={handleClickClose}>
        <DialogTitle>테마 색상 선택</DialogTitle>
        <DialogContent>
          <Stack direction="row" spacing={2}>
            <div>
              Main
              <HexColorPicker color={mainTheme} onChange={handleChangeMain} />
            </div>
            <div>
              Sub
              <HexColorPicker color={subTheme} onChange={handleChangeSub} />
            </div>
          </Stack>
        </DialogContent>
        <DialogActions>
          <Button onClick={handleClickClose}>취소</Button>
          <Button onClick={handleSaveTheme}>테마 저장</Button>
        </DialogActions>
      </Dialog>
    </>
  )
}

reduce 생성

src/store/themeReducer.js

const SET_THEME = 'SET_THEME'

export const setTheme = (mainTheme, subTheme) => ({
  type: SET_THEME,
  mainTheme,
  subTheme,
})

const initialState = { mainTheme: '#4c3c4c', subTheme: '#eee' }

const themeReducer = (state = initialState, action) => {
  switch (action.type) {
    case SET_THEME:
      return {
        mainTheme: action.mainTheme,
        subTheme: action.subTheme,
      }
    default:
      return state
  }
}

export default themeReducer

src/store/index.js

import { combineReducers } from 'redux'
import userReducer from './userReducer'
import channelReducer from './channelReducer'
import themeReducer from './themeReducer'

const rootReducer = combineReducers({
  user: userReducer,
  channel: channelReducer,
  theme: themeReducer,
})

export default rootReducer

Main.jsx 수정

Box컴포넌트의 backgrounColor를 subtheme로 변경

ThemeMenu.jsx를 임포트해줍니다.