React Native Components Example - Digital Watch

React Native Components Example - Digital Watch

Components Example

Introduction

in this example we will review how to create a reusable traditional class components in our application and how to use it.

as explained in the components section here components are used widely in react native development and are a mechanism to segregate our code in smaller re-usable pieces.

We will take our basic digital watch example and try to create a component that will take care of the UI part of our watch.

Source code of basic example without components here:

Source code with components here:

Creating our project

We will be implementing this example with TypeScript so we need to create a basic react project and then do the necessary changes to enable use of TypeScript in our project as described here

in order to speed up the process we can download the basic example for our digital watch already converted to TypeScript here to use it as starting point.

Adding Code

We will create a new page in our project to hold the component code.

the new page will be called WatchComponent.tsx

We will add the required Imports for the components that we will be using.

import React, {Component} from 'react';
import {View, Text, StyleSheet} from 'react-native';

Then we need to define what are the Props for our component,

the purpose of this component is to display the day and time based on the values generated in the main App.tsx page.

so this component will receive two variables in its props as defined below.

interface IOwnProps {
  currentTime: string;
  currentDay: string;
}

in the render method of our new component we will display the values received in Props

public render() {
    return (
      <View style={styles.containerCircle}>
        <View style={styles.CircleShapeView}>
          <Text style={styles.daysText}>{this.props.currentDay}</Text>
          <Text style={styles.timeText}>{this.props.currentTime}</Text>
        </View>
      </View>
    );
  }

in addition to this we will add in the component the required styles as is here where we will display the information.

see below the full code for our component

import React, {Component} from 'react';
import {View, Text, StyleSheet} from 'react-native';
interface IOwnProps {
  currentTime: string;
  currentDay: string;
}
type Props = IOwnProps;
export class WatchComponentView extends Component<Props> {
  constructor(props: Props) {
    super(props);
  }
  public render() {
    return (
      <View style={styles.containerCircle}>
        <View style={styles.CircleShapeView}>
          <Text style={styles.daysText}>{this.props.currentDay}</Text>
          <Text style={styles.timeText}>{this.props.currentTime}</Text>
        </View>
      </View>
    );
  }
}
export const ComponentView = WatchComponentView;
// CSS styles used
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  headerText: {
    fontSize: 30,
    textAlign: 'center',
    margin: 10,
    color: 'black',
    fontWeight: 'bold',
  },
  timeText: {
    top: 90,
    left: 60,
    fontSize: 25,
    color: '#C8C8C8',
  },
  daysText: {
    top: 90,
    left: 60,
    color: '#C8C8C8',
    fontSize: 25,
    paddingBottom: 0,
  },
  containerCircle: {
    top: 200,
    left: 60,
    flex: 1,
  },
  CircleShapeView: {
    width: 270,
    height: 270,
    borderRadius: 580 / 2,
    borderWidth: 10,
    borderColor: '#C8C8C8',
    backgroundColor: '#000000',
  },
});

Then we will modify the App.tsx main page to use our new component.

in the import section we need to import our new component as we will be using it, and we can remove the import to common components that will not be used here any more.

import React, {Component} from 'react';
import {ComponentView} from './WatchComponent';

The rest of the code in App.tsx will be the same as it previously was with the exception of render method, and the fact that we do not need to have the styles definition here any more.

now instead of directly add UI common components here we will use our new component to display the UI passing the required props to it

render() {
    return (
      <ComponentView
        currentTime={this.state.currentTime}
        currentDay={this.state.currentDay}
      />
    );
  }

Below we have the full code of the App.tsx page

import React, {Component} from 'react';
import {ComponentView} from './WatchComponent';
interface IState {
  currentTime: string;
  currentDay: string;
}
type Props = null;
export default class App extends Component<Props, IState> {
  public daysArray: string[] = [
    'sunday',
    'monday',
    'tuesday',
    'wednesday',
    'thursday',
    'friday',
    'saturday',
    'sunday',
  ];
  public timer: any = null;
  constructor(props: Props) {
    super(props);
    this.state = {currentTime: '', currentDay: ''};
  }
  // helper method to get current day and time
  getCurrentTime = () => {
    let hour: string = new Date().getHours().toString();
    let minutes: string = new Date().getMinutes().toString();
    let seconds: string = new Date().getSeconds().toString();
    let am_pm = 'pm';
    if (parseInt(minutes, 10) < 10) {
      minutes = '0' + minutes;
    }
​
    if (parseInt(seconds, 10) < 10) {
      seconds = '0' + seconds;
    }
​
    if (parseInt(hour, 10) > 12) {
      hour = (parseInt(hour, 10) - 12).toString();
    }
​
    if (parseInt(hour, 10) === 0) {
      hour = '12';
    }
​
    if (new Date().getHours() < 12) {
      am_pm = 'am';
    }
​
    this.setState({
      currentTime: hour + ':' + minutes + ':' + seconds + ' ' + am_pm,
    });
​
    this.daysArray.map((item, key) => {
      if (key === new Date().getDay()) {
        this.setState({currentDay: item.toUpperCase()});
      }
    });
  };
  // called before component destroyed
  componentWillUnmount() {
    clearInterval(this.timer);
  }
  // called inmediately after component mounted
  componentDidMount() {
    this.timer = setInterval(() => {
      this.getCurrentTime();
    }, 1000);
  }
  // renders the content for the app
  render() {
    return (
      <ComponentView
        currentTime={this.state.currentTime}
        currentDay={this.state.currentDay}
      />
    );
  }
}
​

In some components we may encounter the need to update certain values back to the caller component,

we can have situations where we use a component to select or pick some piece of information that then needs to flow back to the caller component,

we can achieve this by using state of the caller component and handler methods that can be passed as well as props to the child component.

so in our example we can create a dummy variable in App.tsx page state that we want to update from inside our component.

interface IState {
  currentTime: string;
  currentDay: string;
  dummy: string;
}

We will then create a handler method to manage the state update

public handleStateChange(value: string) {
    this.setState({dummy: value});
  }

In order to be able to use this method in the child component we need to bind the handler to the App.tsx component.

we can do this by adding the bind instruction in our constructor as we can see in line 4 below

constructor(props: Props) {
    super(props);
    this.state = {currentTime: '', currentDay: '', dummy: ''};
    this.handleStateChange = this.handleStateChange.bind(this);
  }

Then in our component we will define a new prop that will actually be a method as we can see below in line 4

interface IOwnProps {
  currentTime: string;
  currentDay: string;
  handleStateChange: (value: string) => void;
}

This method can be used from Props of the component in any place inside the component passing a value, when called, the passed value will be updated in the caller component state, making it available.

in our example we will call it in componentDimMount of the component

public componentDidMount() {
    this.props.handleStateChange('test');
  }

Finally in our App.tsx page we need to pass the new prop to the component as we can see in line 6 below

render() {
    return (
      <ComponentView
        currentTime={this.state.currentTime}
        currentDay={this.state.currentDay}
        handleStateChange={this.handleStateChange}
      />
    );
  }

Below we have the full code of our example after adding the handler method

this is the App.tsx page

import React, {Component} from 'react';
import {ComponentView} from './WatchComponent';
interface IState {
  currentTime: string;
  currentDay: string;
  dummy: string;
}
type Props = null;
export default class App extends Component<Props, IState> {
  public daysArray: string[] = [
    'sunday',
    'monday',
    'tuesday',
    'wednesday',
    'thursday',
    'friday',
    'saturday',
    'sunday',
  ];
  public timer: any = null;
  constructor(props: Props) {
    super(props);
    this.state = {currentTime: '', currentDay: '', dummy: ''};
    this.handleStateChange = this.handleStateChange.bind(this);
  }
  // helper method to get current day and time
  getCurrentTime = () => {
    let hour: string = new Date().getHours().toString();
    let minutes: string = new Date().getMinutes().toString();
    let seconds: string = new Date().getSeconds().toString();
    let am_pm = 'pm';
    if (parseInt(minutes, 10) < 10) {
      minutes = '0' + minutes;
    }
​
    if (parseInt(seconds, 10) < 10) {
      seconds = '0' + seconds;
    }
​
    if (parseInt(hour, 10) > 12) {
      hour = (parseInt(hour, 10) - 12).toString();
    }
​
    if (parseInt(hour, 10) === 0) {
      hour = '12';
    }
​
    if (new Date().getHours() < 12) {
      am_pm = 'am';
    }
​
    this.setState({
      currentTime: hour + ':' + minutes + ':' + seconds + ' ' + am_pm,
    });
​
    this.daysArray.map((item, key) => {
      if (key === new Date().getDay()) {
        this.setState({currentDay: item.toUpperCase()});
      }
    });
  };
  // called before component destroyed
  componentWillUnmount() {
    clearInterval(this.timer);
  }
  // called inmediately after component mounted
  componentDidMount() {
    this.timer = setInterval(() => {
      this.getCurrentTime();
    }, 1000);
  }
  public handleStateChange(value: string) {
    this.setState({dummy: value});
  }
  // renders the content for the app
  render() {
    return (
      <ComponentView
        currentTime={this.state.currentTime}
        currentDay={this.state.currentDay}
        handleStateChange={this.handleStateChange}
      />
    );
  }
}
​

And the component

import React, {Component} from 'react';
import {View, Text, StyleSheet} from 'react-native';
interface IOwnProps {
  currentTime: string;
  currentDay: string;
  handleStateChange: (value: string) => void;
}
​
type Props = IOwnProps;
​
export class WatchComponentView extends Component<Props> {
  constructor(props: Props) {
    super(props);
  }
  public componentDidMount() {
    this.props.handleStateChange('test');
  }
  public render() {
    return (
      <View style={styles.containerCircle}>
        <View style={styles.CircleShapeView}>
          <Text style={styles.daysText}>{this.props.currentDay}</Text>
          <Text style={styles.timeText}>{this.props.currentTime}</Text>
        </View>
      </View>
    );
  }
}
​
export const ComponentView = WatchComponentView;
// CSS styles used
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  headerText: {
    fontSize: 30,
    textAlign: 'center',
    margin: 10,
    color: 'black',
    fontWeight: 'bold',
  },
  timeText: {
    top: 90,
    left: 60,
    fontSize: 25,
    color: '#C8C8C8',
  },
  daysText: {
    top: 90,
    left: 60,
    color: '#C8C8C8',
    fontSize: 25,
    paddingBottom: 0,
  },
  containerCircle: {
    top: 200,
    left: 60,
    flex: 1,
  },
  CircleShapeView: {
    width: 270,
    height: 270,
    borderRadius: 580 / 2,
    borderWidth: 10,
    borderColor: '#C8C8C8',
    backgroundColor: '#000000',
  },
});

The whole project can be downloaded here: