user1227793
user1227793

Reputation: 305

Upgrade legacy React 0.59.X code to 0.63.X

I'm trying to upgrade a legacy React Native app (that also uses Redux) to the latest version and I'm following these guidelines:

This is a sample of the old code base:

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { compose } from 'redux'
import { View, Text } from 'react-native'
import { Colors } from '../../Themes'
import FontAwesome from 'react-native-vector-icons/FontAwesome'
import Octicons from 'react-native-vector-icons/Octicons'
import Feather from 'react-native-vector-icons/Feather'
import AntDesign from 'react-native-vector-icons/AntDesign'
import StatusLabel from '../WcGlobals/StatusLabel'
import OrderItemsList from '../WcGlobals/OrderItemsList'
import CurrencySymbols from '../../Constants/CurrencySymbols'
import FullScreenLoader from '../FullScreenLoader'
import { CapabilitiesSelectors } from '../../Redux/CapabilitiesRedux'
import moment from 'moment'
import Menu, {
  MenuTrigger,
  MenuOptions,
  MenuOption,
  renderers
} from 'react-native-popup-menu'

import styles from './Styles/OrderListItemStyle'

const { SlideInMenu } = renderers

class OrderListItem extends Component {
  // // Prop type warnings
  static propTypes = {
    order: PropTypes.object,
    index: PropTypes.number,
    capabilities: PropTypes.object
  }

  statuses = [
    { label: 'On Hold', value: 'on-hold' },
    { label: 'Processing', value: 'processing' },
    { label: 'Completed', value: 'completed' },
    { label: 'Pending', value: 'pending' }
  ]

  getOrderStatus = () => {
    const { order } = this.props
    return this.statuses.filter(
      ({ value }) => {
        if (order.status === 'processing') { return value === 'completed' }
        if (order.status === 'completed') { return false }
        if (order.status === 'on-hold') { return value === 'completed' || value === 'processing' }
        if (order.status === 'pending') { return value !== order.status }
      }
    )
  }

  onSelect = (value) => {
    const { onUpdate, order } = this.props
    onUpdate(order.id, value)
  }

  render () {
    if (this.props.updating) {
      return (<FullScreenLoader />)
    }
    const statuses = this.getOrderStatus()
    // console.log(statuses);
    const { order, index } = this.props
    const date = moment(order.date_created).format('DDMMM YYYY')
    const time = moment(order.date_created).format('hh:mmA')
    if (!this.props.capabilities.order_status_update && statuses.length !== 0) {
      return (
        <Menu name={`order-status-${this.props.index}`} renderer={SlideInMenu} style={this.props.index ? styles.listItemContainer : styles.listItemContainerFirst} onSelect={this.onSelect}>
          <MenuTrigger>
            <View style={styles.listItemRow}>
              <View style={styles.orderNameStatusContainer}>
                <View style={styles.orderNoCustContainer}>
                  <Text style={styles.orderName}>{'#' + order.id}</Text>
                  { (order.customer_id)
                    ? (<Text style={styles.orderCustomer}>{' (By ' + order.billing.first_name + ' ' + order.billing.last_name + ')' }</Text>)
                    : (<Text style={styles.orderCustomer}>{' (By Guest)' }</Text>)
                  }
                </View>
                <View style={styles.orderStatusItemsContainer}>
                  <StatusLabel status={order.status} />
                </View>
              </View>
              <View style={styles.orderProductContainer}>
                <Octicons size={17} name={'package'} color={Colors.secondaryColor} />
                <OrderItemsList items={order.line_items} />
              </View>
              <View style={[styles.orderDateContainer]}>
                <AntDesign size={16.5} name={'clockcircleo'} color={Colors.secondaryColor} /><Text style={styles.orderDate}>{ date + ' | ' + time }</Text>
                <View style={styles.orderCommissionContainer}>
                  <FontAwesome size={17} name={'money'} color={Colors.secondaryColor} /><Text style={styles.orderCommission}> Earnings: {CurrencySymbols[order.currency]}{Math.round(order.vendor_order_details.total_commission * 100) / 100 }</Text>
                </View>
              </View>
            </View>
          </MenuTrigger>
          <MenuOptions customStyles={{ optionText: styles.slideInOption }}>
            <MenuOption key={'00'} value={''} disabled disableTouchable text={'Tap any one of the options below to change the order status'} />
            {statuses.map(({ label, value }) => (<MenuOption key={value} value={value} text={label} />))}
          </MenuOptions>
        </Menu>
      )
    } else {
      return (
        <View style={this.props.index ? styles.listItemContainer : styles.listItemContainerFirst} >
          <View style={styles.listItemRow}>
            <View style={styles.orderNameStatusContainer}>
              <View style={styles.orderNoCustContainer}>
                <Text style={styles.orderName}>{'#' + order.id}</Text>
                { (order.customer_id)
                  ? (<Text style={styles.orderCustomer}>{' (By ' + order.billing.first_name + ' ' + order.billing.last_name + ')' }</Text>)
                  : (<Text style={styles.orderCustomer}>{' (By Guest)' }</Text>)
                }
              </View>
              <View style={styles.orderStatusItemsContainer}>
                <StatusLabel status={order.status} />
              </View>

            </View>
            <View style={styles.orderProductContainer}>
              <Octicons size={17} name={'package'} color={Colors.secondaryColor} /><OrderItemsList items={order.line_items} />
            </View>
            <View style={[styles.orderDateContainer]}>
              <AntDesign size={16.5} name={'clockcircleo'} color={Colors.secondaryColor} /><Text style={styles.orderDate}>{ date + ' | ' + time }</Text>
              <View style={styles.orderCommissionContainer}>
                <FontAwesome size={17} name={'money'} color={Colors.secondaryColor} /><Text style={styles.orderCommission}>Earnings: {CurrencySymbols[order.currency]}{Math.round(order.vendor_order_details.total_commission * 100) / 100 }</Text>
              </View>
            </View>
          </View>
        </View>
      )
    }
  }
}

const mapStateToProps = (state) => {
  return {
    capabilities: CapabilitiesSelectors.getData(state)
  }
}

export default compose(
  connect(mapStateToProps)
)(OrderListItem)

I'm trying to follow this guide: https://react-native-community.github.io/upgrade-helper/?from=0.59.5&to=0.63.2 and this is what I have come up with so far:

import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { compose } from 'redux'
import {Platform, StyleSheet, Text, View} from 'react-native';
import { Colors } from '../../Themes'
import FontAwesome from 'react-native-vector-icons/FontAwesome'
import Octicons from 'react-native-vector-icons/Octicons'
import Feather from 'react-native-vector-icons/Feather'
import AntDesign from 'react-native-vector-icons/AntDesign'
import StatusLabel from '../WcGlobals/StatusLabel'
import OrderItemsList from '../WcGlobals/OrderItemsList'
import CurrencySymbols from '../../Constants/CurrencySymbols'
import FullScreenLoader from '../FullScreenLoader'
import { CapabilitiesSelectors } from '../../Redux/CapabilitiesRedux'
import moment from 'moment'
import Menu, {
  MenuTrigger,
  MenuOptions,
  MenuOption,
  renderers
} from 'react-native-popup-menu'

import styles from './Styles/OrderListItemStyle'

const { SlideInMenu } = renderers

const OrderListItem = ({order,index,capabilities}) => {
//class OrderListItem extends Component {
  // // Prop type warnings
  OrderListItem.propTypes = {
    order: PropTypes.object,
    index: PropTypes.number,
    capabilities: PropTypes.object
  }

  statuses = [
    { label: 'On Hold', value: 'on-hold' },
    { label: 'Processing', value: 'processing' },
    { label: 'Completed', value: 'completed' },
    { label: 'Pending', value: 'pending' }
  ]

  getOrderStatus = () => {
    const { order } = this.props
    return this.statuses.filter(
      ({ value }) => {
        if (order.status === 'processing') { return value === 'completed' }
        if (order.status === 'completed') { return false }
        if (order.status === 'on-hold') { return value === 'completed' || value === 'processing' }
        if (order.status === 'pending') { return value !== order.status }
      }
    )
  }

  onSelect = (value) => {
    const { onUpdate, order } = this.props
    onUpdate(order.id, value)
  }

  render () {
    if (this.props.updating) {
      return (<FullScreenLoader />)
    }
    const statuses = this.getOrderStatus()
    // console.log(statuses);
    const { order, index } = this.props
    const date = moment(order.date_created).format('DDMMM YYYY')
    const time = moment(order.date_created).format('hh:mmA')
    if (!this.props.capabilities.order_status_update && statuses.length !== 0) {
      return (
        <Menu name={`order-status-${this.props.index}`} renderer={SlideInMenu} style={this.props.index ? styles.listItemContainer : styles.listItemContainerFirst} onSelect={this.onSelect}>
          <MenuTrigger>
            <View style={styles.listItemRow}>
              <View style={styles.orderNameStatusContainer}>
                <View style={styles.orderNoCustContainer}>
                  <Text style={styles.orderName}>{'#' + order.id}</Text>
                  { (order.customer_id)
                    ? (<Text style={styles.orderCustomer}>{' (By ' + order.billing.first_name + ' ' + order.billing.last_name + ')' }</Text>)
                    : (<Text style={styles.orderCustomer}>{' (By Guest)' }</Text>)
                  }
                </View>
                <View style={styles.orderStatusItemsContainer}>
                  <StatusLabel status={order.status} />
                </View>
              </View>
              <View style={styles.orderProductContainer}>
                <Octicons size={17} name={'package'} color={Colors.secondaryColor} />
                <OrderItemsList items={order.line_items} />
              </View>
              <View style={[styles.orderDateContainer]}>
                <AntDesign size={16.5} name={'clockcircleo'} color={Colors.secondaryColor} /><Text style={styles.orderDate}>{ date + ' | ' + time }</Text>
                <View style={styles.orderCommissionContainer}>
                  <FontAwesome size={17} name={'money'} color={Colors.secondaryColor} /><Text style={styles.orderCommission}> Earnings: {CurrencySymbols[order.currency]}{Math.round(order.vendor_order_details.total_commission * 100) / 100 }</Text>
                </View>
              </View>
            </View>
          </MenuTrigger>
          <MenuOptions customStyles={{ optionText: styles.slideInOption }}>
            <MenuOption key={'00'} value={''} disabled disableTouchable text={'Tap any one of the options below to change the order status'} />
            {statuses.map(({ label, value }) => (<MenuOption key={value} value={value} text={label} />))}
          </MenuOptions>
        </Menu>
      )
    } else {
      return (
        <View style={this.props.index ? styles.listItemContainer : styles.listItemContainerFirst} >
          <View style={styles.listItemRow}>
            <View style={styles.orderNameStatusContainer}>
              <View style={styles.orderNoCustContainer}>
                <Text style={styles.orderName}>{'#' + order.id}</Text>
                { (order.customer_id)
                  ? (<Text style={styles.orderCustomer}>{' (By ' + order.billing.first_name + ' ' + order.billing.last_name + ')' }</Text>)
                  : (<Text style={styles.orderCustomer}>{' (By Guest)' }</Text>)
                }
              </View>
              <View style={styles.orderStatusItemsContainer}>
                <StatusLabel status={order.status} />
              </View>

            </View>
            <View style={styles.orderProductContainer}>
              <Octicons size={17} name={'package'} color={Colors.secondaryColor} /><OrderItemsList items={order.line_items} />
            </View>
            <View style={[styles.orderDateContainer]}>
              <AntDesign size={16.5} name={'clockcircleo'} color={Colors.secondaryColor} /><Text style={styles.orderDate}>{ date + ' | ' + time }</Text>
              <View style={styles.orderCommissionContainer}>
                <FontAwesome size={17} name={'money'} color={Colors.secondaryColor} /><Text style={styles.orderCommission}>Earnings: {CurrencySymbols[order.currency]}{Math.round(order.vendor_order_details.total_commission * 100) / 100 }</Text>
              </View>
            </View>
          </View>
        </View>
      )
    }
  }
}

const mapStateToProps = (state) => {
  return {
    capabilities: CapabilitiesSelectors.getData(state)
  }
}

export default compose(
  connect(mapStateToProps)
)(OrderListItem)

Now I'm getting the following error when I run my app:

 ERROR    [Error: TransformError SyntaxError: /App/Components/OrderListing/OrderListItem.js: Unexpected token, expected ";" (61:12)

  59 |   }
  60 | 
> 61 |   render () {
     |             ^
  62 |     if (this.props.updating) {
  63 |       return (<FullScreenLoader />)
  64 |     }]

Any idea how I can resolve this?

Upvotes: 0

Views: 203

Answers (1)

Sulthan
Sulthan

Reputation: 130132

Class components still exists in current version of React and there is no reason to replace them completely with functional components. However, if you want to do it, you have to realize that the component is the render function. You cannot have attributes and methods inside a functional component. It's a function, therefore you will need variables and statements.

Also, you should not use this at all.

The basic form is:

const OrderListItem = props => {
   return ...
};

OrderListItem.propTypes = { ... };

In full, it should be similar to the following (not tested).

const OrderListItem = props => {
  const statuses = [
    { label: 'On Hold', value: 'on-hold' },
    { label: 'Processing', value: 'processing' },
    { label: 'Completed', value: 'completed' },
    { label: 'Pending', value: 'pending' }
  ];

  const getOrderStatus = () => {
    const { order } = props
    return statuses.filter(
      ({ value }) => {
        if (order.status === 'processing') { return value === 'completed' }
        if (order.status === 'completed') { return false }
        if (order.status === 'on-hold') { return value === 'completed' || value === 'processing' }
        if (order.status === 'pending') { return value !== order.status }
      }
    )
  };

  const onSelect = (value) => {
    const { onUpdate, order } = props;
    onUpdate(order.id, value)
  };

  if (props.updating) {
    return (<FullScreenLoader />)
  }

    const statuses = getOrderStatus()
    // console.log(statuses);
    const { order, index } = props
    const date = moment(order.date_created).format('DDMMM YYYY')
    const time = moment(order.date_created).format('hh:mmA')
    if (!props.capabilities.order_status_update && statuses.length !== 0) {
      return (
        <Menu name={`order-status-${props.index}`} renderer={SlideInMenu} style={props.index ? styles.listItemContainer : styles.listItemContainerFirst} onSelect={onSelect}>
          <MenuTrigger>
            <View style={styles.listItemRow}>
              <View style={styles.orderNameStatusContainer}>
                <View style={styles.orderNoCustContainer}>
                  <Text style={styles.orderName}>{'#' + order.id}</Text>
                  { (order.customer_id)
                    ? (<Text style={styles.orderCustomer}>{' (By ' + order.billing.first_name + ' ' + order.billing.last_name + ')' }</Text>)
                    : (<Text style={styles.orderCustomer}>{' (By Guest)' }</Text>)
                  }
                </View>
                <View style={styles.orderStatusItemsContainer}>
                  <StatusLabel status={order.status} />
                </View>
              </View>
              <View style={styles.orderProductContainer}>
                <Octicons size={17} name={'package'} color={Colors.secondaryColor} />
                <OrderItemsList items={order.line_items} />
              </View>
              <View style={[styles.orderDateContainer]}>
                <AntDesign size={16.5} name={'clockcircleo'} color={Colors.secondaryColor} /><Text style={styles.orderDate}>{ date + ' | ' + time }</Text>
                <View style={styles.orderCommissionContainer}>
                  <FontAwesome size={17} name={'money'} color={Colors.secondaryColor} /><Text style={styles.orderCommission}> Earnings: {CurrencySymbols[order.currency]}{Math.round(order.vendor_order_details.total_commission * 100) / 100 }</Text>
                </View>
              </View>
            </View>
          </MenuTrigger>
          <MenuOptions customStyles={{ optionText: styles.slideInOption }}>
            <MenuOption key={'00'} value={''} disabled disableTouchable text={'Tap any one of the options below to change the order status'} />
            {statuses.map(({ label, value }) => (<MenuOption key={value} value={value} text={label} />))}
          </MenuOptions>
        </Menu>
      )
    } else {
      return (
        <View style={props.index ? styles.listItemContainer : styles.listItemContainerFirst} >
          <View style={styles.listItemRow}>
            <View style={styles.orderNameStatusContainer}>
              <View style={styles.orderNoCustContainer}>
                <Text style={styles.orderName}>{'#' + order.id}</Text>
                { (order.customer_id)
                  ? (<Text style={styles.orderCustomer}>{' (By ' + order.billing.first_name + ' ' + order.billing.last_name + ')' }</Text>)
                  : (<Text style={styles.orderCustomer}>{' (By Guest)' }</Text>)
                }
              </View>
              <View style={styles.orderStatusItemsContainer}>
                <StatusLabel status={order.status} />
              </View>

            </View>
            <View style={styles.orderProductContainer}>
              <Octicons size={17} name={'package'} color={Colors.secondaryColor} /><OrderItemsList items={order.line_items} />
            </View>
            <View style={[styles.orderDateContainer]}>
              <AntDesign size={16.5} name={'clockcircleo'} color={Colors.secondaryColor} /><Text style={styles.orderDate}>{ date + ' | ' + time }</Text>
              <View style={styles.orderCommissionContainer}>
                <FontAwesome size={17} name={'money'} color={Colors.secondaryColor} /><Text style={styles.orderCommission}>Earnings: {CurrencySymbols[order.currency]}{Math.round(order.vendor_order_details.total_commission * 100) / 100 }</Text>
              </View>
            </View>
          </View>
        </View>
      )
  }
}

OrderListItem.propTypes = {
  order: PropTypes.object,
  index: PropTypes.number,
  capabilities: PropTypes.object
};


const mapStateToProps = (state) => {
  return {
    capabilities: CapabilitiesSelectors.getData(state)
  }
}

export default compose(
  connect(mapStateToProps)
)(OrderListItem)

Of course, now some of your former methods are pointless, for example getOrderStatus can be just:

const statuses = (() => {
    const { order } = this.props
    return statuses.filter(
      ({ value }) => {
        if (order.status === 'processing') { return value === 'completed' }
        if (order.status === 'completed') { return false }
        if (order.status === 'on-hold') { return value === 'completed' || value === 'processing' }
        if (order.status === 'pending') { return value !== order.status }
      }
    )
  })();

(note this is just an anonymous closure, that is called immediately. And in general, it would work better as a switch-case over order.status).

You can also move helper methods completely out of the functional parameters, not passing them props but only the necessary parameters. That will make them easier to debug and maintain.

Upvotes: 1

Related Questions