I am having issues Cannot read properties of undefined (reading 'some') in react when try to search a resort or a hotel
Here is the result
This is before I hit search button:
After hit search button the resort come out correctly:
After select filter Activities and hit search button again:
Code base from my project:
onSubmit for search button:
onSubmit() {
sessionStorage.remove(ReservationService.APPLIED_POINTS);
sessionStorage.remove(ReservationService.BOOKED_RESERVATION);
sessionStorage.remove(ReservationService.CONTRACT_TOOL_TIPS);
sessionStorage.remove(ReservationService.SELECTED_PREFERENCES);
const segments = getUniqueContractSegments(this.state.form);
this.reservationService.search(this.state.form, segments).then((results: Results) => {
let state: AvailabilityState = this.state;
state.filters = results.filters;
state.selectedFilters = this.stripInApplicableFilters(results.filters);
state.searchResults = results.results;
state.showResults = true;
state.hideLoader = true;
state.loadError = false;
state.resultsError = false;
state.selectedMonth = state.form.startDate;
this.setState(state);
}).catch((error: any) => {
if (!handle401Error(error)) {
let state: AvailabilityState = this.state;
state.hideLoader = true;
state.resultsError = true;
state.searchResultsErrorMessage = error.message ? `${error.message}` : "Unspecified error";
this.setState(state);
log.error(`Error: ${JSON.stringify(error)}`);
}
});
this.setState({hideLoader: false, availability: {form: Object.assign({}, this.state.form)}});
sessionStorage.write(ReservationService.OPS_FORM_SESSION_KEY, JSON.stringify(this.state.form));
}
Here is onFilter for filter select in checkbox:
onFilterSelect(name: string, item: any) {
let selectedFilters: SelectedFilters = {...this.state.selectedFilters};
const selectedFilter = {...selectedFilters[name]};
const idx = selectedFilter.filters.indexOf(item);
if (idx > -1) {
selectedFilter.filters = selectedFilter.filters.filter((filterOption: string) => filterOption !== item);
} else {
selectedFilter.filters = [...selectedFilter.filters, item];
// If filter group isn't expanded, expand.
// User could be changing filter from room-type pills.
if (!selectedFilter.expanded) {
selectedFilter.expanded = true;
}
}
selectedFilters[name] = selectedFilter;
this.setState({ selectedFilters });
console.log("Filter: ", selectedFilters)
}
Error:
this error happens when you want to read a property from an object that has no value (is undefined) so there is a simple solution you can put ? before . like Object?.Property it cause when an object is an undefined property does not read
may be there :
const idx = selectedFilter?.filters?.indexOf(item);
if (idx > -1) {
selectedFilter.filters = selectedFilter?.filters?.filter((filterOption: string) => filterOption !== item);
} else {
selectedFilter.filters = [...selectedFilter.filters, item];
Related
RemoveArea(area: models.Area) {
let messages: string[] = [];
messages = messages.concat(this.getMessagesFromDeletingArea(area));
this._translate.get('QuestionGroup_RemoveAreaConfirm',
{ thisCaption: area.caption[this.BlueContext.currentLanguage] }).subscribe(nextCaption => {
this.dialogsService.confirm(nextCaption, messages)
.subscribe(confirmed => {
if (confirmed) {
this._areaService.removeArea(this.dashboardConfiguration, area, this.BlueContext.currentLanguage);
const index = this.areas.findIndex(a => a.id === area.id);
if (index > -1) {
this.areas.splice(index, 1);
//here
this.dashboardConfiguration.widgets.forEach(wAId => {
const allWidgetByAreaId = wAId.widgets.filter(w => w.areaId === area.id);
allWidgetByAreaId.forEach(w => {
w.areaId = null;
});
});
}
}
});
});
}
The filter is working but the const var (allWidgetByAreaId) is undefined and empty so "for each " does not work. Would you please help?
I have actually made two input boxes (cash amount range e.g, From $100 - To $200) to only type numbers with a single period, comma(s) and fixed to 2 decimal places but when I type alphabets or symbols, they are visible but only on the first try. See GIF below:
See GIF
At first, the input boxes can be filled with all characters or symbols but when I click the next input box or somewhere, the input is gone (because they aren't numbers). What I am trying to achieve here is to prevent the input boxes to be filled with letters or other symbols and to strictly be filled with numbers ONLY. What have I done wrong here?
<InputBox
className="input_midsmall"
id={self.state.fields.remittAmtFr.fieldCd}
placeholder="From"
onChange={onChangeField}
value={self.state.formData.remittAmtFr}
inputType="text"
onBlur={onBlurAmount}
onFocus={onFocusAmount}
/>
render() {
function isAmount(value: string) {
// default limit to 12
return /^\d{0,13}(\.\d{0,2})?$/.test(value)
}
const self = this
const { intl } = this.props
const onChangeField = function (this: any, event: any, desc: string) {
const vm = this || event.target
const fieldCd = vm ? vm.id : desc
let value = !_.isUndefined(event.target)? event.target.value : event
const setState = (prevState: any) => {
const formData = prevState.formData
switch (fieldCd) {
case self.state.fields.remittAmtFr.fieldCd:
case self.state.fields.remittAmtTo.fieldCd:
value = isAmount(value) ? value : formData[fieldCd]
break
}
formData[fieldCd] = value
if(_.isEqual(self.state.fields.dateTo.fieldCd, fieldCd) && _.isNull(value)){
FormValidation.clearErrors(prevState.fields, self.state.fields.dateFr.fieldCd)
}
if(_.isEqual(self.state.fields.dateFr.fieldCd, fieldCd) && _.isNull(value)){
FormValidation.clearErrors(prevState.fields, self.state.fields.dateTo.fieldCd)
}
const fields = FormValidation.clearErrors(prevState.fields, fieldCd)
return {
formData,
fields,
}
}
self.setState(setState)
self.props.onClearAll()
self.setupFieldAttr()
}
const onBlurAmount = function (this: any, value: any) {
const fieldCd = value.currentTarget.id
let inputValue = value.currentTarget.value
if (isNaN(inputValue) || inputValue === '') {
inputValue = ''
} else {
inputValue = isAmount(inputValue)
? formatAmount(inputValue)
: inputValue
}
const setState = (prevState: any) => {
const formData = prevState.formData
formData[fieldCd] = inputValue
const fields = FormValidation.clearErrors(prevState.fields, fieldCd)
return {
formData,
fields,
}
}
self.setState(setState)
}
const onFocusAmount = (value: any) => {
const fieldCd = value.currentTarget.id
let inputValue = value.currentTarget.value
inputValue = formatAmountWithoutComma(inputValue)
onChangeField(inputValue, fieldCd)
}
I set the return to null for the component and condition in question to check the data I'm returning and I couldn't find any issues in the coordinates arrays.
I get data as an array of geometry collections containing linestrings that make borders (from OSM's Overpass). Leaflet seems to only accept shapes, features, and featurecollections as inputs. As such, I wrote something to convert each geometry collection to a feature containing a multipolygon and added in a name and ID properties then made it into a featurecollection.
Example of OSM request body
[out:json];relation["name"="Mount Rainier National Park"]["type"="boundary"]; convert item ::=::,::geom=geom(),_osm_type=type(); out geom;
State
// Get boundaries for national lands in state X
const getBoundaries = async (st) => {
try {
// Fetch boundaries
const usStates = new UsaStates({ includeTerritories: true });
// Convert full state/territory name to two character abbrieviation.
let abbr = null;
usStates.states.map((row) => {
if (row.name === st) {
abbr = row.abbreviation;
}
});
// Build array of national land names
let lands = [];
state.locations.map((loc) => {
if (loc.states.includes(abbr)) {
lands.push(loc.fullName);
}
});
// Build Overpass query for boundaries
let query = "[out:json];";
lands.map((location) => {
query += `relation["name"="${location}"]["type"="boundary"]; convert item ::=::,::geom=geom(),_osm_type=type(); out geom;`;
});
const osmRes = await axios.post(
"https://lz4.overpass-api.de/api/interpreter",
query
);
dispatch({
type: GET_BOUNDARIES,
payload: osmRes.data,
});
} catch (err) {
dispatch({
type: TOAST_ERROR,
payload: err,
});
}
};
Reducer
case GET_BOUNDARIES:
let b = [];
let t = null;
action.payload.elements.map((boundary) => {
let a = [];
t = polygonize(boundary.geometry);
t.features.map((feature) => {
a.push(feature.geometry.coordinates[0]);
});
b.push(multiPolygon(a));
b[b.length - 1].properties = {
name: boundary.tags.name,
id: short.generate(),
};
});
b = featureCollection(b);
console.log("Reducer");
return {
...state,
boundaries: b,
loading: false,
};
Component
import React, { useContext} from "react";
import ParksContext from "../context/parks/ParksContext";
import { GeoJSON } from "react-leaflet";
const Boundaries = () => {
const parksContext = useContext(ParksContext);
const { boundaries, target, locations, states } = parksContext;
return target === null &&
Object.keys(states).length > 0 &&
states.constructor === Object ? (
<GeoJSON data={states} key={states}></GeoJSON>
) : target && locations.find((location) => location.id === target) ? (
<GeoJSON
data={
boundaries[
locations.find((location) => location.id === target).fullName
]
}
/>
) : Object.keys(boundaries).length > 0 &&
boundaries.constructor === Object ? (
<GeoJSON data={boundaries} key={boundaries}></GeoJSON>
) : null;
};
export default Boundaries;
I used geojsonlint.com and found an error in my geojson. My coordinates array of arrays had to be in another array. The outermost array allows for a second element: holes.
case GET_BOUNDARIES:
let b = [];
let t = null;
action.payload.elements.map((boundary) => {
let a = [];
t = polygonize(boundary.geometry);
t.features.map((feature) => {
a.push(feature.geometry.coordinates[0]);
});
b.push(multiPolygon([a])); <-- Here
b[b.length - 1].properties = {
name: boundary.tags.name,
id: short.generate(),
};
A button calls the function signAllBrowsed, which contains two other functions:
loadSafetyLetters is a hook that makes a database call for some data and sets it in context
signAll is a hook that tries to access data in context to do something with it
Context is getting set properly, but when signAll accesses it, the data is not updated. Is there a way to access the updated context without directly passing it to the 2nd function? Or is there a way to call a callback once context is updated and accessible? Seems the updated context is only available after a re-render.
The component containing signAllBrowsed and the 2 hooks are siblings.
code in above image:
setModalVisible(true)
const logHeader = 'SafetyLetterHome::SignAllBrowsed'
try {
const response = await loadSafetyLetters(false) // before beginning sign all, get a fresh list of letters from db
if (Configs.SHOW_REQUEST_LOGS) console.log(`${logHeader} response`, response)
if (response === 'no api error') {
await signAll()
navigation.navigate('SafetyLetterSign')
}
} catch (error) {
const errorMessage = error.status && error.status.message ? error.status.message : error
Alert.alert('Database Error', errorMessage)
console.log(`${logHeader}`, errorMessage)
}
}
loadSafetyLetters calls the loadLetters hook:
const [getLetters] = useGetLetters()
const [sortLetters] = useSortLetters()
const [hasAPIError] = useHasAPIError()
const navigation = useNavigation()
const { setModalVisible, setShowSignAll, setSortedLetters, setUnsortedLetters } = useContext(SafetyContext)
const loadLetters = async (sort = true) => {
try {
const response = await getLetters()
const logHeader = 'SafetyHome::loadLetters'
const errorMessage = 'The following error occurred when trying to load letters:'
if (Configs.SHOW_REQUEST_LOGS) console.log(`${logHeader} response`, response)
const error = hasAPIError(response, logHeader, errorMessage)
if (error) return error
const { data } = response.data.payload
let unsortedLetters = []
if (data !== null && data.length > 0) {
data.map((item) => {
// grab only unsigned letters
if (
item.assignmentStatus === SafetySources.PENDING ||
item.assignmentStatus === SafetySources.BROWSED ||
item.assignmentStatus === SafetySources.QUESTIONS_COMPLETE
) {
unsortedLetters.push({
safetyLetterId: item.safetyLetterId,
title: item.title,
assignmentStatus: item.assignmentStatus,
filePath: item.filePath,
embeddableToken: item.embeddableToken,
sponsorId: item.sponsorId,
letterDate: item.letterDate,
form16: item.form16Enabled === '1' ? true : false,
sponsorName: item.sponsorName,
type: item.letterType,
sortOrder: item.sortOrder, // dear doctor; sortOrder === 1
})
}
})
}
if (unsortedLetters.length > 0) {
let bletters = unsortedLetters.filter((letter) => letter.assignmentStatus === SafetySources.BROWSED || letter.assignmentStatus === SafetySources.QUESTIONS_COMPLETE)
console.log('useLoadLetters; setting fresh pull of letters in context, including ', bletters.length, ' browsed letters')
setUnsortedLetters(unsortedLetters) // set in context
setShowSignAll( // show/hide sign all button
unsortedLetters.some((letter) =>
letter.assignmentStatus === SafetySources.BROWSED ||
letter.assignmentStatus === SafetySources.QUESTIONS_COMPLETE,
))
}
if (sort) {
if (unsortedLetters.length > 0) {
let sortedLetters = sortLetters(unsortedLetters) // sort letters with hook
setSortedLetters(sortedLetters) // set in context
}
}
} catch (error) {
console.log('SafetyHome::loadLetters ', error)
const errorMessage = error.status && error.status.message ? error.status.message : error
Alert.alert(
'Error Loading Letters',
`A database error has occurred. Please try again. (${errorMessage})`,
)
navigation.navigate('Home')
} finally {
setModalVisible(false)
}
}
return [loadLetters]
}
signAll hook:
const { state: { unsortedLetters },
setF16Browsed,
setQcAndBrowsed,
setModalVisible,
setSelectedLetter
} = useContext(SafetyContext)
const signAll = async () => {
let qcAndBrowsed = [] // set letter groups in context
let f16Browsed = []
unsortedLetters.forEach((letter) => {
if (
letter.assignmentStatus === SafetySources.BROWSED ||
letter.assignmentStatus === SafetySources.QUESTIONS_COMPLETE
) {
if (
letter.form16 &&
letter.assignmentStatus !== SafetySources.QUESTIONS_COMPLETE
) {
f16Browsed.push(letter)
} else {
qcAndBrowsed.push(letter)
}
}
})
setQcAndBrowsed(qcAndBrowsed)
setF16Browsed(f16Browsed)
// begin sign all with first f16 letter
if (f16Browsed.length > 0) {
setSelectedLetter(f16Browsed[0])
} else {
setSelectedLetter(null) // clear any previous viewed letter
}
setModalVisible(false)
}
return [signAll]
}
Trying to call addChildNodeNext method in addChildNode function, but
result = this.addChildNodeNext(item.childrens,CurrentID)
gives error of this is undefined. I have already bind both function in constructor.
class TestAdd extends Component {
constructor(props) {
super(props)
this.addChildNode = this.addChildNode.bind(this)
this.addChildNodeNext = this.addChildNodeNext.bind(this)
}
addChildNodeNext = (nodeList, CurrentID) => {
alert(`Self ${nodeList} : ${CurrentID}`)
return nodeList
}
addChildNode = (nodeList, CurrentID) => {
const { childIndex } = this.state
let index = childIndex
const newTree = nodeList.filter(function (item) {
alert(`Self ${item.name} : ${CurrentID}`)
index += 1
let result = ""
if (item.name === CurrentID) {
const newName = `child_${childIndex}_${CurrentID}`
result = item.childrens.push({ name: newName, parent: newName, childrens: [] })
} else if (item.childrens.length > 0) {
result = this.addChildNodeNext(item.childrens, CurrentID)
} else {
result = item
}
return result
});
this.setState({ childIndex: index })
this.setState({ treeNode: newTree })
}
}
export default TestAdd;
You are using a regular function in your .filter method. This is why you lose this context there. Also, you don't need to bind your functions in the constructor because you are using arrow functions.
addChildNode = (nodeList, CurrentID) => {
const { childIndex } = this.state
let index = childIndex
const newTree = nodeList.filter(function (item) { // <--- HERE
alert(`Self ${item.name} : ${CurrentID}`)
index += 1
let result = ""
if (item.name === CurrentID) {
const newName = `child_${childIndex}_${CurrentID}`
result = item.childrens.push({ name: newName, parent: newName, childrens: [] })
} else if (item.childrens.length > 0) {
result = this.addChildNodeNext(item.childrens, CurrentID)
} else {
result = item
}
return result
});
this.setState({ childIndex: index })
this.setState({ treeNode: newTree })
}
You can replace it with an arrow function:
addChildNode = (nodeList, CurrentID) => {
const { childIndex } = this.state
let index = childIndex
const newTree = nodeList.filter(item => {
alert(`Self ${item.name} : ${CurrentID}`)
index += 1
let result = ""
if (item.name === CurrentID) {
const newName = `child_${childIndex}_${CurrentID}`
result = item.childrens.push({ name: newName, parent: newName, childrens: [] })
} else if (item.childrens.length > 0) {
result = this.addChildNodeNext(item.childrens, CurrentID)
} else {
result = item
}
return result
});
this.setState({ childIndex: index })
this.setState({ treeNode: newTree })
}