본문 바로가기
코딩생활/ReactNative

[React Native] 3-12 달력 기능 구현하기 (1)

by InfoJun 2023. 3. 31.
반응형

마지막 구성할 화면은 달력화면 입니다.

 

react-native-calendars라는 라이브러리를 사용합니다.

$ yarn add react-native-calencars

 

components/CalendarView.js

import React from 'react';
import {StyleSheet} from 'react-native';
import {Calendar} from 'react-native-calendars/src/index';

function CalendarView() {
  return <Calendar style={styles.calendar} />;
}

const styles = StyleSheet.create({
  calendar: {
    borderBottomWidth: 1,
    borderBottomColor: '#e0e0e0',
  },
});

export default CalendarView;

 

screens/CalendarScreen.js

import React from 'react';
import CalendarView from '../components/CaendarView';

function CalendarScreen() {
  return <CalendarView />;
}

export default CalendarScreen;

 

이제 달력 탭으로 가면 달력이 보이나요?


달력에 표시하기

지금은 달력에 아무런 표시도 없습니다.

 

앞으로 사용자가 달력에서 특정 날짜를 선택해 해당 날짜에 작성된 로그를 조회하는 기능을 구현할 것입니다.

 

이 기능을 구현하기 위해서는 사용자가 현재 선택한 날짜, 로그를 작성한 날짜에 특별한 표시를 해주겠습니다.

 

추가로 달력에 사용되는 색상들도 변경하겠습니다.

 

components/CalendarView.js

import React from 'react';
import {StyleSheet} from 'react-native';
import {Calendar} from 'react-native-calendars/src/index';

function CalendarView() {
  const markedDates = {
    '2023-03-25': {
      selected: true,
    },
    '2023-03-26': {
      selected: true,
    },
    '2023-03-27': {
      selected: true,
    },
  };

  return (
    <Calendar
      style={styles.calendar}
      markedDates={markedDates}
      theme={{
        selectedDayBackgroundColor: '#009688',
        arrowColor: '#009688',
        dotColor: '#009688',
        todayTextColor: '#009688',
      }}
    />
  );
}

const styles = StyleSheet.create({
  calendar: {
    borderBottomWidth: 1,
    borderBottomColor: '#e0e0e0',
  },
});

export default CalendarView;

 

달력에 표시하기 위해 markedDates라는 객체를 사용합니다.

 

이 객체에 들어가는 키 값은 날짜의 'yyyy-MM-dd' 형태입니다.

 

표시를 원하는 각 날짜에 객체를 만들고, 거기에 marked 값을 true로 설정하면 날짜에 점이 나타납니다.

 

selected 값을 true로 설정하면 이 날짜를 선택했다는 의미로 날짜의 배경색을 변경합니다.

 

표시하는 색상은 theme이라는 Props를 통해 변경할 수 있습니다.


데이터를 달력과 연동하기

달력에 표시하는 법을 알았으니, 데이터의 날짜를 표시하도록 하겠습니다.

 

우선 CalendarScreen에서 LogContext의 logs 배열을 받아오겠습니다.

 

screens/CalendarScreen.js

import React ,{useContext} from 'react';
import CalendarView from '../components/CaendarView';
import LogContext from "../contexts/LogContext";

function CalendarScreen() {
  const {log} = useContext(LogContext);
  return <CalendarView />;
}

export default CalendarScreen;

 

다음으로 이 logs배열을 Calendar 컴포넌트의 markedDates Props 형태로 변환해줍니다.

 

변환된 객체는 CalendarView에 전달하겠습니다.

 

screens/CalendarScreen.js

import React, {useContext} from 'react';
import CalendarView from '../components/CaendarView';
import LogContext from '../contexts/LogContext';
import {format} from 'date-fns';

function CalendarScreen() {
  const {logs} = useContext(LogContext);
  const markedDates = logs.reduce((acc, current) => {
    const formattedDate = format(new Date(current.date), 'yyyy-mm-dd');
    acc[formattedDate] = {marked: true};
    return acc;
  }, {});

  return <CalendarView markedDateds={markedDates} />;
}

export default CalendarScreen;

 

reduce 라는 함수는 배열의 내장함수입니다.

 

배열 안 값을 연산해 하나의 결과를 도출해낼 때 사용합니다.

const array = [0,1,2,3,4,5,6,7,8,10];
const sum = array.reduce((acc,current) => {
	return acc+current;
} , 0 );

 

다음의 예시 코드를 보면 reduce는 첫번째 파라미터로

 

배열의 각 원소를 사용해 특정하는 값을 연산하는 함수를 넣습니다.

 

두번째 파라미터로 초깃값을 넣습니다.

 

첫 번째 파라미터에 넣은 함수의 acc는 accumulato를 의미하는데 ,이는 누적된 값이라는 뜻입니다.

 

current 는 현재 처리중인 값을 가르킵니다.

 

이 함수에서 반환한 값은 다음 원소를 처리할 때 acc 가 됩니다.

 

const markedDates = logs.reduce((acc, current) => {
  const formattedDate = format(new Date(current.date), 'yyyy-mm-dd');
  acc[formattedDate] = {marked: true};
  return acc;
}, {});

 

다음 함수를 사용하는 초깃값은 비어있는 객체입니다.

 

그리고 각 원소의 날짜를 'yyyy-mm-dd'로 형태를 변환 후 변환된 값을 객체의 key로 사용해

 

{marked: true} 값을 지정해줍니다.

 

이렇게하면 로그가 작성된 날짜에만 해당 객체 값이 지정되어

 

로그 작성 날짜가 달력에 표시될 것입니다.

 

다음으로 selectDate라는 상태를 만들겠습니다.

 

이 값은 현재 선택된 날짜를 의미하며 기본값은 오늘 날짜입니다.

 

screens/CalendarScreen.js

import React, {useContext, useState} from 'react';
import CalendarView from '../components/CaendarView';
import LogContext from '../contexts/LogContext';
import {format} from 'date-fns';

function CalendarScreen() {
  const {logs} = useContext(LogContext);

  const [selectDate, setSelectDate] = useState(
    format(new Date(), 'yyyy-mm-dd'),
  );

  const markedDates = logs.reduce((acc, current) => {
    const formattedDate = format(new Date(current.date), 'yyyy-mm-dd');
    acc[formattedDate] = {marked: true};
    return acc;
  }, {});

  return (
    <CalendarView
      markedDateds={markedDates}
      selectedDate={selectDate}
      onSelectDate={setSelectDate}
    />
  );
}

export default CalendarScreen;

 

selectedDate와 setSelectDate 또한 CalendarView에 전달합니다.

 

setSelectedDate는 onSelectDate라는 이름으로 설정하겠습니다.

 

그 다음 CalendarView에서 markedSelectedDate라는 새로운 객체를 만들어 

 

현재 날짜에 selected: true를 설정하겠습니다.

 

components.CalendarView.js

import React from 'react';
import {StyleSheet} from 'react-native';
import {Calendar} from 'react-native-calendars/src/index';

function CalendarView({markedDates, selectedDate, onSelectDate}) {
  const markedSelectedDate = {
    ...markedDates,
    [selectedDate]: {
      selected: true,
      marked: markedDates[selectedDate]?.marked,
    },
  };

  return (
    <Calendar
      style={styles.calendar}
      markedDates={markedSelectedDate}
      theme={{
        selectedDayBackgroundColor: '#009688',
        arrowColor: '#009688',
        dotColor: '#009688',
        todayTextColor: '#009688',
      }}
    />
  );
}
(...)

 

이제 달력에서 날짜를 선택했을 때 onSelectDate를 호출해야합니다.

 

이는 Calendar에 onDayPress라는 Porps를 설정해 구현하겠습니다.

 

components/CalendarVeiw.js - return

return (
  <Calendar
    style={styles.calendar}
    markedDates={markedSelectedDate}
    onDayPress={day => {
      onSelectDate(day.dateString);
    }}
    theme={{
      selectedDayBackgroundColor: '#009688',
      arrowColor: '#009688',
      dotColor: '#009688',
      todayTextColor: '#009688',
    }}
  />
);

 

여기서 day 객체의 구성은 다음과 같습니다.

{
	"dateString" : "2023-03-30:,
    "day" : 30,
    "month" : 3,
    "timestamp" : 1680220800000,
    'year' : 2023
}
반응형

댓글