1
\$\begingroup\$

I've been building a two-sided (both clients and service providers would use the app) appointment booking app. This is a screen from which a service provider can confirm their profile after having completed the fields across multiple screens previously.

I have a couple of React/React Native specific questions:

  • Is it okay to have components defined within parent components that access the parent component's props directly?
  • Is it okay to have onPress handlers defined directly in the JSX directly?

I would also appreciate any and all general feedback. Tear it up!

// ProviderConfirmProfileScreen.js

import React, { Component } from 'react';
import { ScrollView, Text, TouchableOpacity } from 'react-native';
import { ListItem } from 'react-native-elements';
import { connect } from 'react-redux';

import {
  mapStateToProps,
  mapDispatchToProps,
} from '../providerConfirmProfileScreen';
import {
  PersonRow, PhoneRow, LocationRow, LicenseRow,
} from './ClickableRow';
import FullWidthButton from './FullWidthButton';
import { clearStackAndPush } from './navigationHelpers';
import ProfileImage from './ProfileImage';
import ScreenContainerView from './ScreenContainerView';
import screens from './screens';
import { s } from './styles';

class ProviderConfirmProfileScreen extends Component {
  static navigationOptions = () => ({
    title: 'Confirm Profile',
  });

  next = this.props.navigation.getParam('next');

  currentScreen = screens.ProviderConfirmProfileScreen;

  pushModal = (nextScreen) => {
    const next = { [nextScreen]: this.currentScreen };
    this.props.navigation.push(nextScreen, { next });
  };

  ProfileImageRow = () => (
    <TouchableOpacity
      activeOpacity={0.8}
      style={[s.mt1, s.mh1, s.mb1]}
      onPress={() => this.pushModal(screens.ProviderPickProfileImageScreen)}
    >
      <ProfileImage source={this.props.profileImage} />
    </TouchableOpacity>
  );

  BioRow = () => (
    <ListItem
      bottomDivider
      chevron
      onPress={() => this.pushModal(screens.ProviderAddBioScreen)}
      title={<Text style={[s.fs2, s.lh25]}>{this.props.bio}</Text>}
    />
  );

  render() {
    const { navigation } = this.props;
    return (
      <ScreenContainerView>
        <ScrollView showsVerticalScrollIndicator={false}>
          <this.ProfileImageRow />
          <PersonRow
            title={this.props.fullName}
            topDivider
            onPress={() => {
              this.pushModal(screens.ProviderSignupAdditionalFieldsScreen);
            }}
          />
          <PhoneRow
            title={this.props.phone}
            onPress={() => {
              this.pushModal(screens.ProviderSignupAdditionalFieldsScreen);
            }}
          />
          <LocationRow
            title={this.props.city}
            onPress={() => {
              this.pushModal(screens.ProviderSignupAdditionalFieldsScreen);
            }}
          />
          <LicenseRow
            title={this.props.license}
            onPress={() => {
              this.pushModal(screens.ProviderPickLicenseScreen);
            }}
          />
          <this.BioRow />
          <FullWidthButton
            buttonStyle={[s.mt1, s.mb1, s.mh1]}
            title="Confirm"
            onPress={() => {
              this.props.saveProfile();
              const nextScreen = this.next.ProviderConfirmProfileScreen;
              const action = clearStackAndPush(nextScreen, { next: this.next });
              navigation.dispatch(action);
            }}
          />
        </ScrollView>
      </ScreenContainerView>
    );
  }
}

export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(ProviderConfirmProfileScreen);

// styles.js

const SPACING_0 = 10;
const SPACING_1 = 20;

const GRAY = '#666';
const LIGHT_GRAY = '#ddd';

export const SCALE_FACTOR = Dimensions.get('window').scale / 3; // 3 is the iPhone 8 Plus scale factor, do not change

export const s = StyleSheet.create({
  bbw1: { borderBottomWidth: 1 },
  boclgray: { borderColor: LIGHT_GRAY },
  cgray: { color: GRAY },
  clgray: { color: LIGHT_GRAY },
  clcoral: { color: 'lightcoral' },
  ffPingFangHKL: { fontFamily: 'PingFangHK-Light' },
  flex1: { flex: 1 },
  fs0: { fontSize: 25 * SCALE_FACTOR },
  fs1: { fontSize: 21 * SCALE_FACTOR },
  fs2: { fontSize: 17 * SCALE_FACTOR },
  fs3: { fontSize: 13 * SCALE_FACTOR },
  fwb: { fontWeight: 'bold' },
  lh25: { lineHeight: 25 * SCALE_FACTOR },
  mb0: { marginBottom: SPACING_0 },
  mb1: { marginBottom: SPACING_1 },
  mh1: { marginHorizontal: SPACING_1 },
  mt0: { marginTop: SPACING_0 },
  mt1: { marginTop: SPACING_1 },
  mv1: { marginVertical: SPACING_1 },
  pb0: { paddingBottom: SPACING_0 },
  pb1: { paddingBottom: SPACING_1 },
  ph1: { paddingHorizontal: SPACING_1 },
  pt1: { paddingTop: SPACING_1 },
  tac: { textAlign: 'center' },
});

// screens.js

export default {
  ClientAppointmentDetailScreen: 'ClientAppointmentDetailScreen',
  ClientAppointmentListScreen: 'ClientAppointmentListScreen',
  ClientAppointmentRequestSubmittedScreen:
    'ClientAppointmentRequestSubmittedScreen',
  ClientAppointmentTabStackNavigator: 'ClientAppointmentTabStackNavigator',
  ClientConfirmAppointmentRequestScreen:
    'ClientConfirmAppointmentRequestScreen',
  ClientHomeScreen: 'ClientHomeScreen',
  ClientHomeTabStackNavigator: 'ClientHomeTabStackNavigator',
...
\$\endgroup\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.