Статьи

Как создать собственный компонент календаря React

React Native — это кроссплатформенная среда разработки мобильных приложений с плавным обучением и множеством встроенных компонентов. Поскольку у него очень активное сообщество разработчиков, для него также доступны сотни сторонних библиотек компонентов с открытым исходным кодом, позволяющих создавать приложения для Android и iOS, ориентируясь только на основную логику приложений. Тем не менее, возможность быстро создавать свои собственные, повторно используемые компоненты с нуля все еще является важным навыком.

  • Мобильное приложение
    9 лучших шаблонов приложений React Native в 2019 году
    Нона Блэкман
  • React Native
    Начало работы с собственным шаблоном приложения MStore Pro React
    Кайл Слока-Фрей
  • реагировать
    9 шаблонов React Native для изучения и использования
    Эрик Дай
  • React Native
    5 Реагируйте на собственные наборы пользовательского интерфейса, темы и шаблоны приложений
    Кайл Слока-Фрей
  • React Native
    Создание приложений электронной коммерции с помощью собственного шаблона MStore Pro React

В этом руководстве я покажу вам, как создать собственный компонент React Native Calendar, используя только ES6 и несколько базовых компонентов, предлагаемых самой платформой.

Чтобы не устанавливать React Native CLI и все его зависимости на вашем компьютере, на данный момент я предлагаю вам использовать Expo Snack , бесплатную браузерную IDE для разработки приложений React Native. Если у вас еще нет учетной записи Expo, создайте ее сейчас.

После входа в Expo создайте новый проект Snack, перейдя на вкладку Snacks и нажав ссылку « Создать закуску» .

Среда IDE займет всего несколько секунд, чтобы создать проект и подготовить для него устройство предварительного просмотра. Когда все будет готово, оно должно выглядеть так:

Закусочная IDE

Прежде чем продолжить, убедитесь, что вы удалили весь пример кода, присутствующий в App.js.

Чтобы иметь возможность использовать инфраструктуру React и компоненты React Native в своем проекте, добавьте следующие операторы import в начале файла App.js :

1
2
import * as React from ‘react’;
import * as RN from ‘react-native’;

Вы создаете пользовательский компонент React, создавая класс, расширяющий класс Component . Внутри класса вы должны добавить метод с именем render() , который возвращает код JSX. Следующий код создает компонент с именем MyCalendar :

1
2
3
4
5
6
7
8
9
class MyCalendar extends React.Component {
  render() {
    return (
      <RN.View>
       
      </RN.View>
    );
  }
}

В методе render() мы в настоящее время возвращаем пустой компонент View . Он будет служить контейнером для всех других компонентов нашего календаря.

Компоненту Calendar нужны два строковых массива: один для хранения названий месяцев и один для хранения названий дней недели.

1
2
3
4
5
6
7
months = [«January», «February», «March», «April»,
«May», «June», «July», «August», «September», «October»,
«November», «December»];
 
weekDays = [
    «Sun»,»Mon»,»Tue»,»Wed»,»Thu»,»Fri»,»Sat»
];

Далее нам понадобится массив, в котором хранится количество дней в каждом месяце. Для февраля пусть число будет 28. Мы напишем код для обработки високосных лет спустя.

1
nDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

Чтобы сделать наш компонент календаря интерактивным, мы должны связать с ним состояние. Сейчас мы собираемся хранить только объект Date внутри состояния, инициализированный до сегодняшней даты.

1
2
3
state = {
    activeDate: new Date()
}

Состояние, конечно, изменчиво. Когда пользователь нажимает на другую дату в календаре, мы меняем состояние, чтобы использовать новую дату.

Матрица с семью строками и семью столбцами достаточно велика для представления любого месяца года. Мы будем использовать первую строку только в качестве заголовка, храня в ней названия дней недели. Чтобы создать и инициализировать эту матрицу, создайте новый метод с именем generateMatrix() .

1
2
3
4
5
6
7
generateMatrix() {
    var matrix = [];
    // Create header
    matrix[0] = this.weekDays;
 
    // More code here
}

Прежде чем мы начнем добавлять дни в матрицу, нам нужно определить день начала текущего месяца. Для этого сначала получите год и месяц объекта Date сохраненного в состоянии. Затем создайте новый объект Date используя эти значения и 1 , первый день месяца. Вызвав метод getDay() этого нового объекта, вы получите первый день месяца.

1
2
3
4
var year = this.state.activeDate.getFullYear();
var month = this.state.activeDate.getMonth();
 
var firstDay = new Date(year, month, 1).getDay();

Мы не можем напрямую использовать массив nDays чтобы определить количество дней в текущем месяце. Если февраль месяца, нам нужно вручную добавить дополнительный день при работе с високосными годами. Вот как:

1
2
3
4
5
6
var maxDays = this.nDays[month];
if (month == 1) { // February
  if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
    maxDays += 1;
  }
}

На данный момент у нас есть все данные, которые нам нужны для заполнения остальной части матрицы. В следующем коде показано, как это сделать, используя счетчик, два цикла for и два простых условия if :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
var counter = 1;
for (var row = 1; row < 7; row++) {
  matrix[row] = [];
  for (var col = 0; col < 7; col++) {
    matrix[row][col] = -1;
    if (row == 1 && col >= firstDay) {
      // Fill in rows only after the first day of the month
      matrix[row][col] = counter++;
    } else if (row > 1 && counter <= maxDays) {
      // Fill in rows only if the counter’s not greater than
      // the number of days in the month
      matrix[row][col] = counter++;
    }
  }
}
 
return matrix;

Обратите внимание, что вам нужно явно инициализировать каждый элемент матрицы 7 x 7. Если вы забудете это сделать, первая или последняя строка может содержать менее семи элементов. Это может привести к проблемам при использовании метода map() для циклического прохождения матрицы.

Вернувшись в метод render() , мы должны теперь визуализировать созданную нами матрицу. Поэтому вызовите метод generateMatrix() внутри него.

1
var matrix = this.generateMatrix();

Далее, давайте отобразим год и название текущего месяца, добавив компонент Text к текущему пустому компоненту View . При желании вы можете использовать style опору для добавления стилей к тексту.

1
2
3
4
5
6
7
8
<RN.Text style={{
  fontWeight: ‘bold’,
  fontSize: 18,
  textAlign: ‘center’
}}>
  {this.months[this.state.activeDate.getMonth()]} &nbsp;
  {this.state.activeDate.getFullYear()}
</RN.Text>

Мы будем использовать flexbox для визуализации содержимого каждой строки матрицы. Точнее, для каждой строки мы будем использовать компонент View с его параметрами flex и flexDirection 1 и row соответственно. Кроме того, чтобы гарантировать, что все элементы строки имеют одинаковую ширину, мы установим параметр justifyContent в justifyContent равным justifyContent .

Кроме того, для отображения отдельных элементов матрицы мы снова будем использовать Text компоненты. Изменяя свойство backgroundColor компонентов Text отвечающих за элементы первой строки, мы можем выделить заголовок. Аналогично, если вы хотите выделить воскресенья, используйте свойство color компонентов Text отвечающих за элементы первого столбца.

Наш календарь должен быть в состоянии выделить сегодняшнюю дату или дату, которую пользователь выбирает. Поэтому давайте fontWeight свойство fontWeight с каждым компонентом Text . Мы установим его bold когда его содержимое совпадает с датой в переменной activeDate нашего состояния.

В следующем коде показано, как использовать метод map() в качестве альтернативы циклам for при создании иерархии компонентов для содержимого матрицы:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
var rows = [];
rows = matrix.map((row, rowIndex) => {
  var rowItems = row.map((item, colIndex) => {
    return (
      <RN.Text
        style={{
          flex: 1,
          height: 18,
          textAlign: ‘center’,
          // Highlight header
          backgroundColor: rowIndex == 0 ?
          // Highlight Sundays
          color: colIndex == 0 ?
          // Highlight current date
          fontWeight: item == this.state.activeDate.getDate()
                              ?
        }}
        onPress={() => this._onPress(item)}>
        {item != -1 ?
      </RN.Text>
    );
  });
  return (
    <RN.View
      style={{
        flex: 1,
        flexDirection: ‘row’,
        padding: 15,
        justifyContent: ‘space-around’,
        alignItems: ‘center’,
      }}>
      {rowItems}
    </RN.View>
  );
});

Чтобы действительно отобразить матрицу, теперь вы должны включить rows в JSX, возвращаемые методом render() . Поэтому добавьте следующий код ниже компонента Text отвечающего за отображение года и названия месяца:

1
{ rows }

Возможно, вы заметили, что мы onPress обработчик события onPress с каждым компонентом Text отображающим дату. Мы будем использовать его для обновления переменной activeDate всякий раз, когда пользователи нажимают на дату. Конечно, не забывайте игнорировать Text компоненты, которые либо пусты, либо отвечают за названия дней недели.

Соответственно, добавьте следующий метод в ваш класс:

1
2
3
4
5
6
7
8
_onPress = (item) => {
    this.setState(() => {
      if (!item.match && item != -1) {
        this.state.activeDate.setDate(item);
        return this.state;
      }
    });
};

Наш компонент календаря будет иметь две кнопки с надписью Next и Previous . Эти кнопки при нажатии должны позволять пользователям переходить с одного месяца на другой. Как вы уже догадались, в их обработчиках событий все, что нам нужно сделать, это получить объект activeDate и activeDate или уменьшить его месяц на 1 .

Соответственно, добавьте следующий код в конец JSX, возвращаемого методом render() :

1
2
3
4
<RN.Button title=»Previous»
        onPress={() => this.changeMonth(-1)}/>
<RN.Button title=»Next»
        onPress={() => this.changeMonth(+1)}/>

Затем создайте метод changeMonth() . Внутри него вы должны сначала вызвать метод setState() а затем вызвать метод setMonth() для обновления объекта activeDate .

1
2
3
4
5
6
7
8
changeMonth = (n) => {
    this.setState(() => {
      this.state.activeDate.setMonth(
        this.state.activeDate.getMonth() + n
      )
      return this.state;
    });
}

Наш календарный компонент React Native готов. Чтобы использовать его, просто добавьте его в метод render() вашего класса App .

1
2
3
4
5
export default class App extends React.Component {
  render() {
    return <MyCalendar/>;
  }
}

Если вы запустите свой проект сейчас, вы должны увидеть календарь, который выглядит следующим образом:

Пользовательский компонент календаря

Теперь вы знаете, как создавать и использовать пользовательский компонент календаря React Native, не зависящий от сторонних пакетов. Компонент, который мы создали сегодня, является интерактивным, расширяемым и может использоваться в любом приложении с минимальными изменениями. Не стесняйтесь добавлять больше стилей и функциональности к нему.

Чтобы узнать больше о компонентах React Native, обратитесь к официальной документации . И посмотрите другие наши посты о разработке приложений React Native!

  • Мобильное приложение
    9 лучших шаблонов приложений React Native в 2019 году
    Нона Блэкман
  • React Native
    Начало работы с собственным шаблоном приложения MStore Pro React
    Кайл Слока-Фрей
  • реагировать
    9 шаблонов React Native для изучения и использования
    Эрик Дай
  • React Native
    5 Реагируйте на собственные наборы пользовательского интерфейса, темы и шаблоны приложений
    Кайл Слока-Фрей
  • React Native
    Создание приложений электронной коммерции с помощью собственного шаблона MStore Pro React