我正在设置一个新组件“ SearchResultListItem”,由于某种原因,我得到一个指向“ SearchResult”的错误,并且不清楚我的代码有什么问题。
这是我的依赖项:
"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"
}
现在是SearchResult组件。
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
现在是SearchResultListItem组件。
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
我希望获得产品的输出,但是出现错误消息:
Invariant Violation:Element type is invalid:expected a string..