How to Create a React Native Calendar Component
2022-7-27 10:56:19 Author: code.tutsplus.com(查看原文) 阅读量:35 收藏

React Native is a cross-platform mobile app development framework with a gentle learning curve and lots of built-in components. Because it has a very active developer community, there are also hundreds of open-source third-party component libraries available for it, allowing you to create both Android and iOS apps focusing only on the apps' core logic. Nevertheless, being able to quickly create your own custom, reusable components from scratch is still an important skill to have.

In this tutorial, I'll show you how to create a custom React Native Calendar component using just ES6 and a few basic components offered by the framework itself.

1. Creating a New Project

To avoid installing the React Native CLI and all its dependencies on your computer, for now, I suggest you use Expo's Snack, a free, browser-based IDE for React Native app development. If you don't have an Expo account already, create one now.

After you've logged in to Expo, create a new Snack project by switching to the Snacks tab and clicking on the Create a Snack link.

The IDE will take just a few seconds to create your project and prepare a preview device for it. Once it's ready, it should look like this:

Snack IDESnack IDESnack IDE

Before you proceed, make sure you delete all the sample code present in App.js.

2. Creating a New Component

To be able to use the React framework and React Native components in your project, add the following import statements at the beginning of the App.js file:

import * as React from 'react';
import * as RN from 'react-native';

You create a custom React component by creating a class that extends the Component class. Inside the class, you must add a method named render(), which returns JSX code. The following code creates a component named MyCalendar:

class MyCalendar extends React.Component {
  render() {
    return (
      <RN.View>
      
      </RN.View>
    );
  }
}

In the render() method, we're currently returning an empty View component. It's going to serve as a container for all the other components of our calendar.

3. Creating Constants

The calendar component needs two string arrays: one to store the names of the months and one to store the names of the days of the week.

months = ["January", "February", "March", "April", 
"May", "June", "July", "August", "September", "October", 
"November", "December"];

weekDays = [
    "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
];

Next, we'll need an array that stores the number of days each month has. For February, let the number be 28. We'll write the code to handle leap years later.

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

4. Initialize a State

To make our calendar component interactive, we must associate a state with it. For now, we're going to store nothing but a Date object inside the state, initialized to today's date.

state = {
    activeDate: new Date()
}

The state, of course, is mutable. When a user clicks on a different date in the calendar, we'll be changing the state to use the new date.

5. Generating a Matrix

A matrix with seven rows and seven columns is large enough to represent any month of the year. We'll use the first row only as a header, storing the names of the days of the week in it. To create and initialize this matrix, create a new method named generateMatrix().

generateMatrix() {
    var matrix = [];
    // Create header
    matrix[0] = this.weekDays;

    // More code here
}

Before we start adding days to the matrix, we need to determine the day the current month begins. To do so, first get the year and month of the Date object stored in the state. Then create a new Date object using those values and 1, the first day of the month. By calling the getDay() method of this new object, you get the first day of the month.

var year = this.state.activeDate.getFullYear();
var month = this.state.activeDate.getMonth();

var firstDay = new Date(year, month, 1).getDay();

We can't directly use the nDays array to determine the number of days the current month has. If the month's February, we need to manually add an extra day while dealing with leap years. Here's how:

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

At this point, we have all the data we need to fill in the rest of the matrix. The following code shows you how to do so using a counter, two for loops, and two simple if conditions:

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;

Note that you need to explicitly initialize every element of the 7 x 7 matrix. If you forget to do so, the first or last row may have less than seven elements. This can lead to problems while using the map() method to loop through the matrix.

6. Rendering a Month

Back inside the render() method, we must now render the matrix we created. So call the generateMatrix() method inside it.

var matrix = this.generateMatrix();

Next, let's display the year and the name of the current month by adding a Text component to the currently empty View component. Optionally, you can use the style prop to add styles to the text.

<RN.Text style={{
  fontWeight: 'bold',
  fontSize: 18,
  textAlign: 'center'
}}>
  {this.months[this.state.activeDate.getMonth()]} &nbsp;
  {this.state.activeDate.getFullYear()}
</RN.Text>

We'll be using a flexbox to render the contents of each row of the matrix. More precisely, for each row, we'll be using a View component with its flex and flexDirection parameters set to 1 and row respectively. Additionally, to ensure that all items of the row are of the same width, we'll set the flexbox's justifyContent parameter to space-around.

Furthermore, to display the individual elements of the matrix, we'll use Text components again. By modifying the backgroundColor property of the Text components responsible for the first row's elements, we can make the header stand out. Similarly, if you want to highlight Sundays, use the color property of the Text components responsible for the first column's elements.

Our calendar should be able to highlight today's date, or a date the user selects. Therefore, let's associate a fontWeight property with each Text component. We'll set it to bold whenever its contents match the date in our state's activeDate variable.

The following code shows you how to use the map() method as an alternative to for loops while generating a component hierarchy for the contents of the matrix:

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 ? '#ddd' : '#fff',
          // Highlight Sundays
          color: colIndex == 0 ? '#a00' : '#000',
          // Highlight current date
          fontWeight: item == this.state.activeDate.getDate() 
                              ? 'bold': ''
        }}
        onPress={() => this._onPress(item)}>
        {item != -1 ? item : ''}
      </RN.Text>
    );
  });
  return (
    <RN.View
      style={{
        flex: 1,
        flexDirection: 'row',
        padding: 15,
        justifyContent: 'space-around',
        alignItems: 'center',
      }}>
      {rowItems}
    </RN.View>
  );
});

To actually render the matrix, you must now include rows in the JSX returned by the render() method. So add the following code below the Text component responsible for displaying the year and name of the month:

{ rows }

You may have noticed that we've associated an onPress event handler with each Text component displaying a date. We'll use it to update the activeDate variable whenever users click on a date. Of course, remember to ignore Text components that are either empty or responsible for the names of the days of the week.

Accordingly, add the following method to your class:

_onPress = (item) => {    
    this.setState(() => {
      if (!item.match && item != -1) {
        this.state.activeDate.setDate(item);
        return this.state;
      }
    });
};

7. Changing Months

Our calendar component will have two buttons labeled Next and Previous. These buttons, when pressed, should allow users to move from one month to another. As you may have guessed, inside their event handlers, all we need to do is get the activeDate object and increment or decrement its month by 1.

Accordingly, add the following code towards the end of the JSX returned by the render() method:

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

Next, create the changeMonth() method. Inside it, you must first call the setState() method and then call the setMonth() method to update the activeDate object.

changeMonth = (n) => {
    this.setState(() => {
      this.state.activeDate.setMonth(
        this.state.activeDate.getMonth() + n
      )
      return this.state;
    });
}

8. Using the Component

Our React Native calendar component is ready. To use it, just add it to the render() method of your App class.

export default class App extends React.Component {
  render() {
    return <MyCalendar/>;
  }
}

If you run your project now, you should see a calendar that looks like this:

Custom calendar componentCustom calendar componentCustom calendar component

Conclusion

You now know how to create and use a custom React Native calendar component without depending on any third-party packages. The component we created today is interactive, extensible, and can be used in any app with minimal changes. Feel free to add more styles and functionality to it.

To learn more about React Native components, refer to the official documentation. And check out some of our other posts about React Native app development!

Did you find this post useful?

Ashraff Hathibelagal

Hathibelagal is an independent Android app developer and blogger who loves tinkering with new frameworks, SDKs, and devices.


文章来源: https://code.tutsplus.com/tutorials/how-to-create-a-react-native-calendar-component--cms-33664
如有侵权请联系:admin#unsafe.sh