首页 > 解决方案 > Expo 独立 android 应用程序在某些设备上崩溃

问题描述

我已经为 iOS 和 Android 创建了一个应用程序,在 iOS 的 testflight 以及模拟器(iOS 和 Android)中一切正常。我为 android 创建了一个构建 (APK) 并将其安装在多个设备上。在大多数设备上一切正常,但在某些 Android 设备上,应用程序在进入某个屏幕后崩溃并重新启动。如果我从该屏幕上删除所有代码并留下一个空白容器,它可以正常工作,如果我从屏幕上随机删除组件并且我删除的内容并不重要,它也可以工作。

这是一件非常奇怪的事情,因为当我在设备上使用具有调试模式的模拟器或 expo 客户端时,我没有收到任何错误消息,因此很难理解问题所在。

这是导致问题的页面中的代码。我没有选择,所以希望有人可以帮助我。

import React from "react";
import {fas} from "@fortawesome/pro-solid-svg-icons";
import { library } from '@fortawesome/fontawesome-svg-core';
import {withTranslation} from "react-i18next";
import styled from "styled-components";
import {colors} from "../../../../../assets/styles/variables";
import {Form, Toast, View} from "native-base";
import {Field, formValueSelector, reduxForm, getFormValues} from "redux-form";
import {connect} from "react-redux";
import {KeyboardAwareScrollView} from "react-native-keyboard-aware-scroll-view";
import RelationsSelector from "../../../../shared/components/selectors/relations/Selector";
import RegularTextInput from "../../../../shared/components/form/RegularTextInput";
import {fetchContacts, fetchRelation} from "../../../relations/actions";
import DateComponent from "./DateComponent";
import PaymentConditions from "../../../../shared/components/selectors/payment-conditions/PaymentConditions";
import LinesComponent from "./LinesComponent";
import {
  calculateLineTotals,
  calculateTotals,
  editInvoiceLine,
  getSalesInformation,
  saveInvoice,
  updateInvoice
} from "../actions";
import PickerInput from "../../../../shared/components/form/PickerInput";
import {LogBox} from "react-native"
import isEqual from 'lodash/isEqual';
import RichTextInput from "../../../../shared/components/form/RichTextInput";
import DateInput from "../../../../shared/components/form/DateInput";
import SwitchInput from "../../../../shared/components/form/SwitchInput";
import TextItem from "../../../../shared/components/form/TextItem";
import ContactsSelector from "../../../../shared/components/selectors/contacts/Selector";
import MultiPickerInput from "../../../../shared/components/form/MultiPickerInput";
import {saveQuotation, updateQuotation} from "../../../quotations/actions";

LogBox.ignoreAllLogs(true)

library.add( fas );

class EditComponent extends React.Component {

  state = {
    isLoading: false,
    selectedLine: null,
    paymentDays: 0,
    validRelationVat: false,
    footer_text: "",
    noteOffset: 0,
    footerTextOffset: 0,
    address: "",
    initialPaymentCondition: this.props.initialValue ? true : false
  }

  async componentDidMount() {
     await this.props.getSalesInformation(this.props.type);

     let template;
     let currency;

     // Set default template
     if(!this.props.initialValue && this.props.templates) {
       template = this.props.templates.find(template => template.default === 1) || this.props.templates[0];
       this.props.change("template_id", template.id);
     }

     if(!this.props.initialValue?.id) {
       this.props.change("create_update_id", 0)
       this.props.change("full_number", "Concept");
     }

     if(this.props.invoice.concept === "1") {
       this.props.change("full_number", "Concept");
     }

    if(!this.props.initialValue && this.props.currencies) {
      currency = this.props.currencies.find(currency => currency.default === 1) || this.props.currencies[0];
      this.props.change("currency_id", currency.id)
      this.props.change("currency_rate", currency.rate)
    }

    if(!this.props.initialValue && this.props.attachments) {
      const attachments = this.props.attachments.map(attachment => attachment.id);
      this.props.change("attachments", attachments);
    }

    if(this.props.initialValue?.id) {
      this.calculateTotals();
      this.props.change("create_update_id", this.props.initialValue.id);
    }

    if(this.props.settings.tax_option && this.props.taxOptions?.length > 0) {
      const selectedOption = this.props.taxOptions.find(option => option.value === this.props.settings.tax_option);
      if(this.props.invoice && !this.props.invoice.tax_option) {
        this.props.change("tax_option", selectedOption.value);
      }
    }
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    if(this.props.onFormSave && this.props.onFormSave !== prevProps.onFormSave) {
      this.onHandleSaveInvoice();
    }

    // If tax option is changing we have to edit the lines with the right line tax and ledger
    if(prevProps.invoice && prevProps.invoice.tax_option && this.props.invoiceLines && (this.props.invoice.tax_option !== prevProps.invoice.tax_option)) {

      const prevTaxOptions = this.props.taxOptions.find(option => option.value === prevProps.invoice.tax_option);
      const taxOptions = this.props.taxOptions.find(option => option.value === this.props.invoice.tax_option);

      if(!isEqual(prevTaxOptions.options, taxOptions.options)) {
        setTimeout(function(){
          alert(this.props.t("warning_change_tax"));
        }.bind(this), 1000);
      }

      this.onHandleChangeLines();
    }
  }

  componentWillUnmount() {
    this.props.onCloseForm();
  }

  onHandleChangeLines() {
    if(this.props.invoice.line) {
      const taxOption = this.props.taxOptions.find(option => option.value === this.props.invoice.tax_option);
      const currentTaxOption = this.props.tax.find(option => option.id === taxOption.options[0]);

      const ledgers = this.props.ledgers.filter(option => currentTaxOption ? option.report_code === currentTaxOption.report_code : option).map(option => {
        return {
          label: option.description,
          value: option.id
        }
      });
      this.props.invoice.line.map(async(line, index) => {
        line.tax_id = currentTaxOption.id;
        line.ledger_account_id = ledgers[0].value;

        const invoiceLine = await this.props.calculateLineTotals(line, this.props.taxOptions, true);
        this.props.editInvoiceLine(index, invoiceLine);
      });

      this.calculateTotals();
    }

  }

  calculateTotals() {
    if(this.props.taxOptions) {
      this.props.calculateTotals(this.props.taxOptions);
    }
  }


  addNewLine() {
    this.setState({selectedLine: null})
    this.props.navigation.navigate("InvoicesLinesForm");
  }

  selectLine(id) {
    this.setState({selectedLine: id});
    const selectedLine = this.props.invoice.line[0];
    this.props.navigation.navigate('InvoicesLinesForm', {invoiceLineId: id, ledgerId: selectedLine.ledger_account_id});
  }

  async onHandleSaveInvoice() {
    let response;

    if(this.props.invoice && this.props.invoice.id) {

      // Update existing invoice
      if(this.props.type === "sales_invoice") {
        response = await this.props.updateInvoice(this.props.invoice.id);
      }
      if(this.props.type === "quotation") {
        response = await this.props.updateQuotation(this.props.invoice.id);
      }
    } else {
      // Save new Invoice
      if(this.props.type === "sales_invoice") {
        response = await this.props.saveInvoice();
      }
      if(this.props.type === "quotation") {
        response = await this.props.saveQuotation();
      }
    }

    if(response) {
      if(response.success) {
        Toast.show({
          text: response.message,
          duration: 3000,
          type: "success"
        });
        this.props.reset();
        this.props.onSaveSuccess(response.id);
      }

      if(response.error) {
        if(Array.isArray(response.error)) {
          Toast.show({
            text: response.error.join('\n'),
            buttonText: this.props.t("button_okay"),
            duration: 5000,
            type: "danger"
          });
        } else {
          Toast.show({
            text: response.error,
            duration: 5000,
            type: "danger"
          });
        }
      }
    } else {
      alert(this.props.t("something_went_wrong"))
    }

    this.props.onHandleSave(response);
  }

  async onSelectRelation(id) {
    if(id) {
      const relation = this.props.relation;
      this.props.change("relation_id", id);
      this.setState({address: relation.street + " " + relation.house_number + ", " + relation.zip });

      if(relation.payment_condition && !this.state.initialPaymentCondition) {
        if(this.props.paymentConditions.find(option => option.id === relation.payment_condition)) {
          this.props.change("payment_condition_id", relation.payment_condition);
          this.onChangePaymentCondition(relation.payment_condition);
          this.setState({initialPaymentCondition: false});
        }
      }

      if(relation.vat_valid) {
        this.setState({validRelationVat: true});
      } else {
        this.setState({validRelationVat: false});
      }
    }
    this.props.fetchContacts({relation_id: id});
  }

  getTaxOptions() {
    if(this.props.taxOptions) {
      const taxOptions = this.state.validRelationVat ? this.props.taxOptions :  this.props.taxOptions.filter(option => option.vat_check === 0);

      const taxValues = taxOptions.map(option => {
        return {
          label: option.name,
          value: option.value
        }
      });

      return taxValues;
    }
    return [];
  }

  getTemplates() {
    if(this.props.templates) {

      return this.props.templates.map(option => {
        return {
          label: option.description,
          value: option.id
        }
      });

    }
    return [];
  }

  getAttachments() {
    if(this.props.attachments) {

      return this.props.attachments.map(option => {
        return {
          label: option.description,
          value: option.id
        }
      });

    }
    return [];
  }

  getCurrencies() {
    if(this.props.currencies) {

      return this.props.currencies.map(option => {
        return {
          label: option.currency,
          value: option.id
        }
      });

    }
    return [];
  }

  onChangePaymentCondition(id) {
    if(this.props.paymentConditions) {
      const paymentCondition = this.props.paymentConditions.find(item => item.id === id);

      if(this.props.type === "sales_invoice") {
        this.setState({paymentDays: paymentCondition.days, footer_text: JSON.parse(paymentCondition.footer_text_invoice)});
      }
      if(this.props.type === "quotation") {
        this.setState({paymentDays: paymentCondition.days, footer_text: JSON.parse(paymentCondition.footer_text_quotation)});
      }
    }
  }

  onHandleChangeCurrency(value) {
    const currency = this.props.currencies.find(currency => currency.id === value);
    if(currency) {
      this.props.change("currency_rate", currency.rate);
      this.calculateTotals();
    }
  }



  render() {

    return (
      <Container>
        <List>
          <KeyboardAwareScrollView scrollEventThrottle={16} showsVerticalScrollIndicator={false}>
            <Form>
              <Content>
                <Field
                  name="relation_id"
                  label={this.props.t("relation")}
                  onChange={(id) => this.onSelectRelation(id)}
                  placeholder={this.props.t("make_choice")}
                  component={RelationsSelector}
                />
                <TextItem
                  value={this.state.address}
                  label={this.props.t("address")}
                  placeholder={this.props.t("street") + ", " + this.props.t("zip")}
                />

                <Field
                  name="contact_name"
                  label={this.props.t("contact")}
                  placeholder={this.props.t("contact")}
                  multiline={false}
                  component={ContactsSelector}
                  isSearchContactsAvailable={this.props.invoice?.relation_id && this.props.contacts.length > 0}
                />

                <Field
                  name="reference"
                  label={this.props.t("reference")}
                  placeholder={this.props.t("reference")}
                  multiline={false}
                  component={RegularTextInput}
                />
                <Field
                  name="full_number"
                  label={this.props.t("number")}
                  placeholder={this.props.t("number")}
                  multiline={false}
                  component={RegularTextInput}
                  disabled
                />
                <DateComponent paymentDays={this.state.paymentDays}/>

                <Field
                  name="tax_option"
                  label={this.props.t("tax_options")}
                  placeholder={this.props.t("make_choice")}
                  data={this.getTaxOptions()}
                  component={PickerInput}
                  last
                />
              </Content>
              <Separator>
                <SeparatorText>{this.props.t("invoice_lines")}</SeparatorText>
              </Separator>
              <LinesComponent
                onHandleNewLine={()=> this.addNewLine()}
                onHandleSelectLine={(id)=> this.selectLine(id)}
                invoice={this.props.invoice}
                lines={this.props.invoiceLines}
              />

              <Separator>
                <SeparatorText>{this.props.t("note")}</SeparatorText>
              </Separator>

              <Content style={{marginTop: 10}} onLayout={({nativeEvent}) => this.setState({noteOffset: nativeEvent.layout.y})}>
                <Field
                  name="comment"
                  label={this.props.t("note")}
                  placeholder={this.props.t("note_placeholder")}
                  props={this.props}
                  component={RichTextInput}
                  onFocusInput={() => this.scroll.props.scrollToPosition(0, this.state.noteOffset)}
                  last
                />
              </Content>

              <Separator>
                <SeparatorText>{this.props.t("footer_text")}</SeparatorText>
              </Separator>
              <Content style={{marginTop: 10}} onLayout={({nativeEvent}) => this.setState({footerTextOffset: nativeEvent.layout.y})}>
                <Field
                  name="footer_text"
                  label={this.props.t("footer_text")}
                  placeholder={this.props.t("footer_text_placeholder")}
                  props={this.props}
                  component={RichTextInput}
                  html={this.state.footer_text}
                  onFocusInput={() => this.scroll.props.scrollToPosition(0, this.state.footerTextOffset)}
                  last
                />
              </Content>


              <Separator>
                <SeparatorText>{this.props.t("extra_options")}</SeparatorText>
              </Separator>
              <Content style={{marginTop: 10}}>
                <Field
                  name="template_id"
                  label={this.props.t("template")}
                  placeholder={this.props.t("make_choice")}
                  data={this.getTemplates()}
                  component={PickerInput}
                />

                {this.props.currencies && this.props.currencies.length > 1 && (
                  <>
                    <Field
                      name="currency_id"
                      label={this.props.t("currency")}
                      placeholder={this.props.t("make_choice")}
                      data={this.getCurrencies()}
                      onChange={(value) => this.onHandleChangeCurrency(value)}
                      component={PickerInput}
                    />
                    <Field
                      name="currency_rate"
                      label={this.props.t("currency_rate")}
                      placeholder={this.props.t("currency_rate")}
                      multiline={false}
                      component={RegularTextInput}
                    />
                  </>
                )}
                {this.props.attachments && this.props.attachments.length > 0 && (
                  <Field
                    name="attachments"
                    label={this.props.t("attachments")}
                    placeholder={this.props.t("make_choice")}
                    component={MultiPickerInput}
                    items={this.getAttachments()}
                  />
                )}

                {this.props.initialValue?.id && this.props.type === "sales_invoice" && (
                  <Field
                    name="dispute"
                    label={this.props.t("dispute")}
                    placeholder={this.props.t("dispute")}
                    explanation={this.props.t("dispute_explanation")}
                    multiline={false}
                    component={SwitchInput}
                  />
                )}

                {this.props.invoice?.dispute === 1 && (
                  <Field
                    name="dispute_date"
                    label={this.props.t("dispute_date")}
                    placeholder={this.props.t("dispute_date")}
                    explanation={this.props.t("dispute_date_explanation")}
                    multiline={false}
                    component={DateInput}
                  />
                )}

                <View style={{height: 1, backgroundColor: "white", marginTop: -1}}></View>
              </Content>

            </Form>
          </KeyboardAwareScrollView>
        </List>
      </Container>
    )
  }
}

const mapStateToProps = (state, props) => {
  const selector = formValueSelector('salesInvoiceEditForm');
  const invoiceSelector = getFormValues('salesInvoiceEditForm');
  return {
    initialValues: props.initialValue,
    relations: state.relations.results,
    relation: state.relations.relation,
    contacts: state.relations.contacts,
    taxOptions: state.salesInvoices.configuration.tax_options,
    tax: state.salesInvoices.configuration.tax,
    templates: state.salesInvoices.configuration.templates,
    currencies: state.salesInvoices.configuration.currencies,
    attachments: state.salesInvoices.configuration.attachments,
    ledgers: state.salesInvoices.configuration.ledgers,
    paymentConditions: state.masterData.paymentConditions,
    settings: state.config.settings,
    invoice: invoiceSelector(state),
    invoiceLines: selector(state, "line"),
  };
};

EditComponent = connect(
  mapStateToProps, {
    fetchRelation,
    fetchContacts,
    getSalesInformation,
    editInvoiceLine,
    calculateLineTotals,
    calculateTotals,
    saveInvoice,
    updateInvoice,
    saveQuotation,
    updateQuotation
  }
)(reduxForm({
  form: 'salesInvoiceEditForm',
  enableReinitialize: true
})(EditComponent));

export default withTranslation()(EditComponent);

const Container = styled.View`
  background: ${colors.primary}
  flex: 1;
  padding: 0;
`;

const List = styled.View`
  background: ${colors.greyLight};
  overflow: hidden;
  flex: 1;
`;

const Separator = styled.View`
  margin-left: 15px;
`;

const SeparatorText = styled.Text`
  text-transform: uppercase;
  font-size: 14px;
  color: ${colors.textColor90};
`;

const Content = styled.View`
  margin-top: 30px;
  margin-bottom: 30px;
  background: white;
  border-color: ${colors.greyBorder};
  border-top-width: 1px;
  border-bottom-width: 1px;
`;

标签: javascriptreactjsreact-nativeexpo

解决方案


推荐阅读