I'm setting up a new component "SearchResultListItem" and for some reason, I get an error that points to "SearchResult" and it's not clear what's wrong there with my code.

This is my dependencies :

"dependencies": {
    "bugsnag-react-native": "^2.15.0",
    "lodash": "^4.17.11",
    "mobx": "3.4.0",
    "mobx-persist": "0.4.1",
    "mobx-react": "4.3.5",
    "moment": "^2.24.0",
    "pushwoosh-react-native-plugin": "^5.13.1",
    "rc-form": "^2.4.3",
    "react": "16.8.3",
    "react-native": "0.59.1",
    "react-native-camera": "^2.2.0",
    "react-native-config": "^0.11.7",
    "react-native-confirmation-code-input": "^1.0.4",
    "react-native-device-info": "^1.4.3",
    "react-native-gesture-handler": "~1.0.14",
    "react-native-i18n": "^2.0.15",
    "react-native-modal-selector": "^1.0.3",
    "react-native-permissions": "^1.1.1",
    "react-native-pixel-perfect": "^1.0.2",
    "react-native-vector-icons": "^6.4.1",
    "react-navigation": "^3.4.0",
    "tipsi-stripe": "^7.4.0",
    "uuid": "^3.3.2"
}

now it the SearchResult component.

import React, { Component } from 'react'
import { View, Text, FlatList, TextInput } from 'react-native'
import { SearchResultListItem, Spinner } from '../../components'
import style from './style'
import Api from '../../utils/Api'
import I18n from '../../i18n'
import { observer, inject } from 'mobx-react/native'
import _ from 'lodash'
import { PRODUCT_REQ_LIMIT } from '../../utils/Consts'
import moment from 'moment'
let mounted = false
@inject('NavigationStore', 'UserStore')
@observer
class SearchResult extends Component {
  constructor(props) {
    super(props)
    this.state = {
      searchTerm: props.navigation.state.params.isNewProducts ? true : props.navigation.state.params.search,
      tagSearch: props.navigation.state.params.tags,
      products: [],
      filteredProducts: [],
      loading: false,
      refreshing: false,
      currentCategory: props.navigation.state.params.title,
      isNewProducts: props.navigation.state.params.isNewProducts,
      totalFound: 0,
      noMoreToFind: false,
      filter: '',
      clicked: false,
    }
    this.isLoading = false
    this.failedCount = 0
    this.lastRequest = 0
    this.page = 0

    this.debounce = _.debounce(() => this.getData(false), 500)
  }

  componentDidMount() {
    let isNew = this.props.navigation.state.params.isNewProducts
    // GaHit.pageView(isNew ? 'NewProducts' : 'SearchResult')
    //Mixpanel.trackWithProperties('Lookup_value', { component: 'SearchResult', value_looked_up: this.props.navigation.state.params.search })
  }

  componentWillMount() {
    mounted = true
    this.getData(false)
  }

  componentWillUnmount() {
    mounted = false
    this.setState({ products: [] })
    clearTimeout(this.failedTimeout)
  }
  sortByDate = arr => {
    return arr.sort((a, b) => {
      var dateA = new Date(a.created_at)
      var dateB = new Date(b.created_at)
      return dateA - dateB
    })
  }

  async getData(nextPage) {
    if (this.isLoading || !mounted || (!this.state.isNewProducts && this.state.noMoreToFind)) return
    this.isLoading = true
    let sortByDate = typeof searchTerm === 'boolean'
    if (mounted) this.setState({ loading: true })
    let searchTerm = this.state.isNewProducts && this.state.filter.length ? this.state.filter : this.state.searchTerm

    nextPage ? this.page++ : (this.page = 0)
    try {
      const response = typeof searchTerm === 'string' ? await Api.search(searchTerm, this.page) : typeof searchTerm === 'boolean' ? await Api.getNewProducts(60, this.page, this.state.filter) : this.state.tagSearch ? await Api.searchByTag(searchTerm, this.page) : await Api.searchByCategory(searchTerm, this.page)
      this.failedCount = 0
      if (mounted) {
        await this.setState({ totalFound: response.total })
        if (!response.products || response.products.length === 0) {
          this.setState({ loading: false, noMoreToFind: true })
          //  Mixpanel.trackWithProperties('Lookup_value_No_match', { component: 'SearchResult', value_looked_up: this.props.navigation.state.params.search, date: moment() })
          this.isLoading = false
          return
        }
        let products = response.products
        //Mixpanel.trackWithProperties('Lookup_value_match', { component: 'SearchResult', list_of_values: products.map(item => item.name).join(','), type_of_values: 'product', amount_of_mutches: this.state.totalFound })
        if (this.page !== 0) products = this.state.products.concat(products)
        if (sortByDate) products = this.sortByDate(products)
        this.setState({ products, loading: false, noMoreToFind: false }, () => (this.isLoading = false))
      }
    } catch (error) {
      console.log('error', error)
      if (mounted) {
        this.failedCount++
        this.setState({ loading: false })
        this.isLoading = false
        if (this.failedCount > 10)
          this.failedTimeout = setTimeout(() => {
            this.failedCount = 0
            if (mounted) this.getData(true)
          }, 5000)
        else this.getData(true)
      }
    }
  }

  getMoreData = () => {
    this.getData(true)
  }

  renderProductListItem = (item, disabled) => (
    <SearchResultListItem
      onPress={selectedPackIndex => {
        if (!this.state.clicked) {
          this.setState({ clicked: true }, () =>
            setTimeout(() => {
              if (mounted) this.setState({ clicked: false })
            }, 300)
          )
          this.props.NavigationStore.navigate({ routeName: 'ProductInfo', params: { product: item, fromScan: false, selectedPackIndex } })
        }
      }}
      popupRef={this.popupRef}
      {...item}
      disabled={disabled}
    />
  )

  onListRefresh = () => {
    this.setState({ refreshing: true, noMoreToFind: false })
    this.getData().then(() => {
      if (mounted) this.setState({ refreshing: false })
    })
  }
  filterProducts = () => {
    return this.state.products.filter(product => {
      return product.product_title.toLowerCase().includes(this.state.filter.toLowerCase()) || product.warehouse.warehouse_name.toLowerCase().includes(this.state.filter.toLowerCase())
    })
  }

  isLastPage = () => {
    return Math.ceil(this.state.totalFound / PRODUCT_REQ_LIMIT) == this.page + 1
  }

  render() {
    I18n.locale = this.props.UserStore.user.lang
    let { products, loading, refreshing, totalFound, searchTerm } = this.state
    const currentCategory = this.state.currentCategory
    if (!this.state.isNewProducts && this.state.filter.length > 0) {
      products = this.filterProducts()
    }
    const disabled = this.state.clicked
    return (
      <View style={style.container}>
        <View style={style.foundView}>
          <Text style={style.foundText}>
            {loading && products.length === 0 ? I18n.t('searching') : `${totalFound ? totalFound : products.length === 0 ? '0' : ''} ${I18n.t('product')}${products.length !== 1 ? 's' : ''} ${I18n.t('found')}`}{' '}
            {currentCategory && products.length ? (
              <Text>
                {I18n.t('in')} <Text style={style.currentCategory}>{currentCategory}</Text>
              </Text>
            ) : null}
          </Text>
        </View>
        {typeof searchTerm === 'boolean' ? (
          <TextInput
            autoCorrect={false}
            style={style.search_input}
            underlineColorAndroid='transparent'
            placeholder={I18n.t('search_products')}
            returnKeyType='search'
            onChangeText={filter => {
              this.setState({ filter })
              this.state.isNewProducts && this.debounce()
            }}
            blurOnSubmit
          />
        ) : null}

        {/* {console.log('products=', products)} */}
        <View style={style.list}>
          <FlatList
            refreshing={refreshing}
            onRefresh={this.onListRefresh}
            onEndReached={() => /*products.length < this.state.totalFound &&*/ !this.isLoading && !this.isLastPage() && mounted && this.getMoreData()}
            onEndReachedThreshold={0.01}
            data={products}
            keyExtractor={(o, i) => i}
            renderItem={({ item }) => this.renderProductListItem(item, disabled)}
            ListFooterComponent={() => {
              //TODO: replace the if cond in 167 with 166 after DBL bug fix
              // if ((this.state.products.length < this.state.totalFound || this.state.loading) && !this.state.refreshing)
              if (this.state.loading && !this.state.refreshing) return <Spinner style={style.loading} />
              return <View style={style.loading} />
            }}
            ItemSeparatorComponent={() => <View style={style.separator} />}
          />
        </View>
      </View>
    )
  }
}

export default SearchResult

now it the SearchResultListItem component.

import React from 'react'
import { View, Text } from 'react-native'
import { LoadingImage } from '../../components/LoadingImage/LoadingImage'
import { Button } from '../../components/Button/Button'
import { NewTag } from '../../components/NewTag/NewTag'
import style from './style'
import I18n from '../../i18n'
import { CDN_URL } from '../../utils/Consts'
import ModalSelector from 'react-native-modal-selector'
import Ionicons from 'react-native-vector-icons/Ionicons'
import { calcSize } from '../../utils/Utils'
import moment from 'moment'

const SearchResultListItem = props => {
  const ProductPrimaryImages = props.images ? props.images.filter(image => image.primary) : []
  if (ProductPrimaryImages.length === 0 && props.images && props.images.length > 0) ProductPrimaryImages.push(props.images[0])

  const caseTypes = props.pack_options.map((item, index) => {
    let label = item.pack_qua > 1 ? item.pack_qua + `-${I18n.t('case')} ${I18n.t('master_case')}` : I18n.t('single_case')
    return {
      key: index,
      quantity: item.pack_qua,
      width: item.pack_width,
      height: item.pack_height,
      len: item.pack_length,
      price: item.price,
      id: item.shp_pack_id,
      is_consolidate: item.is_consolidate,
      label: label + ` at $${item.price.price}`,
    }
  })
  let selected = caseTypes[0].key
  const _onChangeCaseType = async option => {
    selected = option.key
  }
  const compCases = (a, b) => {
    if (parseFloat(a.price.cost_per_case) < parseFloat(b.price.cost_per_case)) return -1
    if (parseFloat(a.price.cost_per_case) > parseFloat(b.price.cost_per_case)) return 1
    return 0
  }

  const onSOGMInfoPress = () => {
    props.popupRef &&
      props.popupRef.setModalVisible(true, {
        product_title: props.product_title,
        sogm: props.sogm,
        min_sogm: props.min_sogm,
        warehouse_name: props.warehouse.warehouse_name,
        is_consolidate: props.conso,
        sogm_qua: props.sogm_qua ? props.sogm_qua : 4,
        sogm_pack: caseTypes.sort(compCases)[0].label,
        bypass: props.warehouse.shp_method_id !== 'ups',
      })
  }
  const newProduct = moment().diff(moment(props.created_at), 'days') <= 60
  return (
    <Button style={style.container} onPress={() => props.onPress(selected)} disabled={props.disabled}>
      {newProduct ? <NewTag search /> : null}

      <View style={style.imageContainer}>
        <LoadingImage resizeMode='contain' source={ProductPrimaryImages.length > 0 ? { uri: CDN_URL + ProductPrimaryImages[0].full_name } : require('../../assets/images/default_no_image.png')} style={props.inventory ? style.image : style.grayscaleImageFront} />
      </View>

      <View style={style.textContainer}>
        <Text style={style.brandName}>{props.warehouse.warehouse_name}</Text>
        <Text style={style.productName}>
          {props.product_title} - {props.unit_type === 'items' ? parseInt(props.unit_size).toString() + ' ' + I18n.t('count') : props.unit_size + ' ' + props.unit_type}
        </Text>
        <View style={style.specialOffer}>{props.specialOffer ? <Text>TODO:special offer</Text> : null}</View>
        {!props.inventory ? (
          <View style={style.inventory_tag}>
            <Text style={style.lastOrderText}>{I18n.t('out_of_stock')}</Text>
          </View>
        ) : null}

        <View style={style.bottomLine}>
          <View style={{ flexDirection: 'column', alignItems: 'center' }}>
            <Text style={props.inventory ? style.productInfoGreen : style.productInfoGrayScale}>${props.srp}</Text>
            <Text style={style.productInfoGrey}>SRP</Text>
          </View>
          <View style={style.barrier} />
          {!props.min_sogm ? (
            <View style={{ flexDirection: 'column', alignItems: 'center' }} onPress={props.min_sogm ? onSOGMInfoPress : () => props.onPress(selected)}>
              <Text style={props.inventory ? style.productInfoGreen : style.productInfoGrayScale}>
                {props.pack_options[0].price.grs}% {props.min_sogm ? <Ionicons name='ios-warning-outline' style={[props.inventory ? style.infoIcon : style.infoIconGrayscale]} /> : null}
              </Text>
              <Text style={style.productInfoGrey}>{I18n.t('cart_item_gross')}</Text>
            </View>
          ) : (
            <Button style={{ flexDirection: 'column', alignItems: 'center' }} onPress={props.min_sogm ? onSOGMInfoPress : () => props.onPress(selected)}>
              <Text style={props.inventory ? style.productInfoGreen : style.productInfoGrayScale}>
                {props.pack_options[0].price.grs}% {props.min_sogm ? <Ionicons name='ios-warning-outline' style={[props.inventory ? style.infoIcon : style.infoIconGrayscale]} /> : null}
              </Text>
              <Text style={style.productInfoGrey}>{I18n.t('cart_item_gross')}</Text>
            </Button>
          )}
          <View style={style.barrier} />
          <View style={{ flexDirection: 'column', alignItems: 'center' }}>
            <Text style={props.inventory ? style.productInfoGreen : style.productInfoGrayScale}>{props.units_per_case}</Text>
            <Text style={[style.productInfoGrey]}>{I18n.t('cart_item_units_per_case')}</Text>
          </View>
        </View>

        <ModalSelector style={style.modalViewCaseType} selectStyle={style.modalSelectStyleCaseType} selectTextStyle={props.inventory ? style.modalSelectTextCaseType : style.modalSelectTextCaseTypeGreyscale} data={caseTypes} disabled={caseTypes.length === 1 || !props.inventory} initValue={caseTypes[0].label} onChange={_onChangeCaseType} />
      </View>
    </Button>
  )
}
export default SearchResultListItem

I expect the output of products but got an error that says :

Invariant Violation:Element type is invalid:expected a string..

0 Answers