Not able to map API result in React Native app - reactjs

I want to return the results to Text elements, but I'm not able to use map on the state that it is stored in.
Here is my code:
const [specData, setSpecData] = useState([]);
const ActionType = ['Existing Construction', 'New Construction'];
const [ actionType, setActionType ] = useState("");
const [ capacity, setCapacity ] = useState("");
const [ seer2, setSeer2 ] = useState("");
const [ eer2, setEer2 ] = useState("");
const [ existingTon, setExistingTon ] = useState("");
var data = `{"specifications": {
"ARI_Reference_No": " ",
"ClimateZone": 2,
"Action_Type_Operation": "${actionType}",
"Condensor_Brand_Name": " ",
"Condensor_Model_No": " ",
"Condensor_Serial_No": " ",
"Capacity_BTUH2": "${capacity}",
"SEER2": "${seer2}",
"EER2": "${eer2}",
"Replaced_Unit_Type": "Central Air Conditioner",
"Existing_BrandName": null,
"Old_Unit_Tonnage": ${existingTon},
"Existing_Model": null,
"Existing_Serial": null,
"ReplacedUnitType": null,
"ManualJ": true,
"Multi_System_Change_Out": false,
"Program_Option": "HEE HVAC Program Option"
}}`
const APICall = async () => {
var xhr = new XMLHttpRequest();
xhr.addEventListener("readystatechange", function () {
if (this.readyState === 4) {
console.log(this.responseText);
const result = this.responseText;
setSpecData(result)
}
});
xhr.open('POST', 'https://api-plus.anbetrack.com/etrm-gateway/etrm/api/v1/etrm/Texas PUC/R-HV-AC/PUCT TRM V10/version/10/execute?token=');
xhr.setRequestHeader('content-type','application/json');
xhr.setRequestHeader('cache-control', 'no-cache');
xhr.send(data);
}
This is how it returns the request from the API:
{
"result": {
"Nominal_New_Unit_Tonnage": 3.5,
"kWh": 3302.9725609756088,
"kW": 1.2162244897959178,
"I": 1344.898672688993
},
"calculationTime": 0
}
I have tried to use map function and set the state as a string and a array, but it still throws the error
undefined is not a function(near'...specData.map...')
This is how I was trying to render it:
<Text>Result {specData.map((res) => {
return(
<Text>{res.result.Nominal_New_Unit_Tonnage}</Text>
)
})}</Text>

Related

Can't use the data from API when app just starts

My data is undefined when the app is started but after the refresh, the data comes perfectly.
For startup
It gives me [Unhandled promise rejection: TypeError: Object.entries requires that input parameter not be null or undefined]
But after the refresh, the data comes perfectly and everything working.
This is part of my data
Object {
"attributes": Object {
"htmlName": null,
"id": 0,
"items": Array [
Object {
"htmlName": "r_1",
"name": "m2 (Brüt)",
"numeric": true,
"options": Object {},
"order": 0,
"required": true,
},
Object {
"htmlName": "r_2",
"name": "m2 (Net)",
"numeric": true,
"options": Object {},
"order": 0,
"required": true,
},
Object {
"htmlName": "r_164",
"name": "Arsa Alanı (m2)",
"numeric": true,
"options": Object {},
"order": 0,
"required": true,
},
Object {
"htmlName": "a_137",
"name": "Oda Sayısı",
"numeric": false,
"options": Object {
"12": "1+0",
"13": "1+1",
"14": "1.5+1",
"15": "2+0",
"16": "2+1",
"17": "2.5+1",
"18": "2+2",
"19": "3+1",
"20": "3.5+1",
"21": "3+2",
"22": "4+1",
"226": "0+1",
"23": "4.5+1",
"24": "4+2",
"25": "4+3",
"26": "4+4",
"27": "5+1",
"28": "5+2",
"29": "5+3",
"30": "5+4",
"31": "6+1",
"32": "6+2",
"33": "6+3",
"34": "7+1",
"35": "7+2",
"36": "7+3",
"37": "8+1",
"38": "8+2",
"39": "8+3",
"40": "8+4",
"41": "9+1",
"42": "9+2",
"43": "9+3",
"44": "9+4",
"45": "9+5",
"46": "9+6",
"47": "10+1",
"48": "10+2",
"49": "10 Üzeri",
},
"order": 0,
"required": true,
},
api.js
export const getData = function () {
return axios
.get(
"blabla",
{
headers: {
Authorization: `blabla`,
},
}
)
.then((json) => {
if (json && json.status === 200) {
//console.log(json);
return json.data;
}
})
.catch((e) => {
console.log(e);
});
};
App.js
const [data, setData] = useState({});
const [roomValue, setRoomValue] = useState(null);
const [roomCount, setRoomCount] = useState([]);
const [isFocus, setIsFocus] = useState(false);
useEffect(() => {
getDataFunc();
//setDropdown(data.attributes.items[3].options);
}, []);
const getDataFunc = async () => {
const res = await getData();
//console.log(res);
setData(res);
console.log(data);
};
function setDropdown(query) {
const response = query;
try {
const entries = Object.entries(response);
const tempArray = [];
for (let i = 0; i < entries.length; i++) {
var key;
var value;
(key = entries[i][0]), (value = entries[i][1]);
tempArray.push({ key: value, value: key });
}
setRoomCount(tempArray);
//console.log(roomCount);
} catch (error) {
//console.log(error);
}
}
How can I fix that ?
Add a seperate useEffect to check wheather the data has been set and then only set the dropdown values
useEffect(() => {
getDataFunc();
}, []);
useEffect(() => {
if(data && data.attributes?.items[3]){
setDropdown(data.attributes.items[3].options);
}
}, [data]);
const getDataFunc = async () => {
const res = await getData();
//console.log(res);
setData(res);
console.log(data);
};
It seems like the error is caused by the attributes property being empty when you try to access it. But when you assign them one by one then it loads because the data is loaded per nested property before assigning it to the variable. Means it hasn't fully loaded yet
const response = data.attributes.items[3].options;
It outputs an error because attributes is undefined. So it's not an object, therefore, attributes.items is considered invalid
// sample
const data = {
/* attributes: {
items: {
1: {
options: 'option1'
},
2: {
options: 'option2'
},
3: {
options: 'option3'
}
}
} */
}
const specificData = data.attributes.items[3].options
console.log(specificData) //
So one solution would be using the optional chaining operator to avoid the error, it's just basically a question mark (?) after the object you are trying to access.
The response would be then 'undefined'. That way even if the attributes is empty or not, data will be assigned to the response constant then you can just add some more checking outside of that.
// sample
const data = {
/* attributes: {
items: {
1: {
options: 'option1'
},
2: {
options: 'option2'
},
3: {
options: 'option3'
}
}
} */
}
const specificData = data.attributes?.items[3].options
console.log(specificData) // outputs undefined instead of an error
Let me know if this works btw. maybe you could provide the actual api or maybe a sample api endpoint so we could test it directly. Or maybe the full code?
I've encoutered this before though I'm not 100% sure this is all I've done. But for the error I'm sure the optional chaining operator will prevent it
Try calling getData inside an async function and wait for the process to complete like this in your App.js
const [data, setData] = useState([]);
const [roomCount, setRoomCount] = useState([]);
useEffect(() => {
getDataFunc()
}, []);
const getDataFunc = async() => {
await getData(setData);
const response = data;
console.log(response);
const entries = Object.entries(response);
const tempArray = [];
for (let i = 0; i < entries.length; i++) {
var key;
var value;
(key = entries[i][0]), (value = entries[i][1]);
tempArray.push({ key: value, value: key });
}
setRoomCount(tempArray);
console.log(roomCount);
}
note: The best practice is not to directly pass the setData function to getData api call instead return the response from api and assign the response in main code like below
const response = await getData();
setData(response)
From what I see, your data.attributes has undefined value.
Please double-check everything, it is technically impossible to get data directly if data.attributes is undefined

React useEffect loop: circular dependency

I'm trying to call a function every time that function's parameters change and this has generated a loop that I don't know how to solve. I've tried implementing this in several ways, such as:
const [fromAmount, setFromAmount] = useState(1);
const [fromToken, setFromToken] = useState<keyof typeof tokens>("WBNB");
const [toToken, setToToken] = useState<keyof typeof tokens>("CAKE");
const path = useMemo(
() => [tokens[fromToken].address, tokens[toToken].address],
[fromToken, toToken]
);
const setAmountsOutParams = useCallback(() => {
if (fromAmount > 0) {
getAmountsOut.setParams({
amountIn: ethers.utils.parseUnits(String(fromAmount), 18),
path,
});
}
}, [fromAmount, path]);
useEffect(() => {
setAmountsOutParams()
}, [setAmountsOutParams]);
As context, I have a hook that provides me with the getAmountsOut, and this is the code regarding it:
const [paramsGetAmountsOut, setParamsGetAmountsOut] = useState({
amountIn: ethers.utils.parseUnits('0', 18),
path: ['', ''],
});
const { data: dataGetAmountsOut } = useContractRead({
...swapper,
functionName: 'getAmountsOut',
args: [paramsGetAmountsOut.amountIn, paramsGetAmountsOut.path],
});
const getAmountsOut = {
data: dataGetAmountsOut,
setParams: setParamsGetAmountsOut,
};
How to solve it?

In react-table with React Hooks, how to make a get request inside Cell?

I have been working with React-table for a couple of days now, my first time using it, and I have ran into some issues I can't quite seem to resolve. I am trying to build a table where I can show data from two API get requests at the same time, and since I don't know if there is a way to connect the two requests data into one object, and I wouldn't know how to do it, I was trying to access some of the data with get requests inside the react-table Column Cell itself.
My case being: I have two objects, Contacts and Institutions, contacts have in their data the institution ID as parameter, and I need to show in the table both the contact information and some information of the institution that is linked to it, getting it from the institution ID that is present in the contact data.
Here is one example of contact:
{
"contact_id": "34378a25-fe8c-4c64-bd35-59eab3f30863",
"institution_id": "ae1d0fe8-cce1-40ef-87d7-729dfbe9716d",
"name": "Contato 2",
"role": "Cargo 1",
"phone_numbers": [],
"emails": [],
"createdAt": "2021-03-09T20:40:26.6863764Z",
"updatedAt": "2021-03-09T20:40:26.686376448Z",
"deleted": false
}
And here is the institution data:
{
"institution_id": "ae1d0fe8-cce1-40ef-87d7-729dfbe9716d",
"name": "Matheus Salles Blanco",
"socialReason": "teste",
"cnpj": "99999999999999",
"abbreviation": "Matheus",
"website": "teste.com",
}
This is the code that is being implemented, reduced to only the parts that matter and that is working, but only showing the info that is being fetched from the contact object:
const Contacts = ({ match }) => {
const [data, setData] = useState([]);
const [institution, setInstitution] = useState();
const dataRecoil = useRecoilValue(contactData);
const handleContact = useCallback(async () => {
const response = dataRecoil.data;
if (response) {
setData(response.filter((contact) => !contact.deleted));
}
}, [setData, dataRecoil]);
useEffect(() => {
handleContact();
}, [handleContact]);
const columns = useMemo(
() => [
{
Header: 'Nome',
accessor: 'name',
},
{
Header: 'Sigla',
accessor: 'abbreviation',
},
{
Header: 'Nome Fantasia',
accessor: 'institution_id',
},
],
[editToggle, handleDelete],
);
return (
<>
<Table columns={columns} data={data} />
</>
);
};
And a print of it:
And here is what I have been trying to do:
const Contacts = ({ match }) => {
const [data, setData] = useState([]);
const [institution, setInstitution] = useState();
const dataRecoil = useRecoilValue(contactData);
const handleContact = useCallback(async () => {
const response = dataRecoil.data;
if (response) {
setData(response.filter((contact) => !contact.deleted));
}
}, [setData, dataRecoil]);
useEffect(() => {
handleContact();
}, [handleContact]);
const columns = useMemo(
() => [
{
Header: 'Nome',
accessor: 'name',
},
{
Header: 'Sigla',
accessor: 'abbreviation',
},
{
Header: 'Nome Fantasia',
accessor: 'institution_id',
Cell: async ({ cell }) => {
const response = await getInstitutionById(cell.row.values.institution_id);
const result = [response.data];
const inst = result.map((inst) => {return inst.name});
const institution_name = inst[0];
console.log(institution_name);
return institution_name;
},
},
],
[editToggle, handleDelete],
);
return (
<>
<Table columns={columns} data={data} />
</>
);
};
Which works at the level of fetching the right data, but does not render the page and shows errors:
The error
The right data being shown in the console.log
The expected output would be to show those names on the console.log on place of that long ID of the first picture.
So, is it possible to do what I am trying to do? And if so, what might am I be doing wrong?
I believe the issue is that you are providing an async function for your cell, which will return a Promise, not the institution name as you are expecting.
A potential solution is to instead create a custom Cell component that uses state to store the institution name. I have provided an example below, which was guided by this example, however I have not tested the code at all, so use it as more of a guide.
const MyCell = ({ cell }) => {
const [institutionName, setInstitutionName] = useState('fetching...')
useEffect(() => {
const getInstitutionName = async (id) => {
const response = await getInstitutionById(id);
const result = [response.data];
const inst = result.map((inst) => {return inst.name});
const institution_name = inst[0];
console.log(institution_name);
setInstitutionName(institution_name)
}
getInstitutionName(cell.row.values.institution_id)
}
return institutionName
}
const Contacts = ({ match }) => {
const [data, setData] = useState([]);
const [institution, setInstitution] = useState();
const dataRecoil = useRecoilValue(contactData);
const handleContact = useCallback(async () => {
const response = dataRecoil.data;
if (response) {
setData(response.filter((contact) => !contact.deleted));
}
}, [setData, dataRecoil]);
useEffect(() => {
handleContact();
}, [handleContact]);
const columns = useMemo(
() => [
{
Header: 'Nome',
accessor: 'name',
},
{
Header: 'Sigla',
accessor: 'abbreviation',
},
{
Header: 'Nome Fantasia',
accessor: 'institution_id',
Cell: MyCell
},
],
[editToggle, handleDelete],
);
return (
<>
<Table columns={columns} data={data} />
</>
);
};

Connecting 2 different arrays from the same external link - React hooks fetch

I am able to fetch data what url, but the thing is the url is divided into couple of arrays and I need to fetch data and connect them.
Example:
{
"array1": [
{ "data1": {"name": "Name", "phone": "Phone"}}
]
"array2" : [
{ "data2": { "color": "Color", "car": "Car" } }
]
}
Data hooks :
const userInfo = "URL";
const [userData, setUserData] = useState([]);
useEffect(() => {
getUserInfo();
}, []);
const getUserInfo = async () => {
const response = await fetch(UserInfo);
const jsonData = await response.json();
setUserData(jsonData);
};
Fetch data:
{ userData.data && userData.array1.map((array1, index) =>
<li key={"index" + index}
<h5>{array1.data1.name} </h5>
</li>
)}
I need to connect name from array1 with color from array2, but I can not find the way to do it.
Expected Output : list of data
If you can get those two arrays then you can use this to combine them:
const getUserInfo = async () => {
const response = await fetch(UserInfo);
const jsonData = await response.json();
// this assumes `jsonData` in an object with keys `array1` and `array2`
// if this is not the case, change `jsonData` below to the location of
// those two arrays
const { array1, array2 } = jsonData;
const combinedArray = array1.map(({ data1 }, i) => ({
...data1,
...array2[i].data2 // assumes there is always a corresponding index in array 2
}));
// combinedArray will look like [ { name: 'Name', phone: 'Phone', color: 'Color', car: 'Car' } ] 
setUserData(combinedArray);
};
] 

useState does not update the state

I'm trying to update a state using useState hook, however the state won't update. I've checked how to fix it but really have no idea about it what cause this point. This is the whole code I didnt include the urls and import files...
When onchange method trigger ilceZoom function event has value so ı can get it evt.value example values is "1234" but I can not set it using useState future
const ilceUrl = 'URL';
const AddressSearchMaks = (props) => {
useEffect(() => {
ilceLoad();
}, []);
const [ ilceler, setIlceler ] = useState([]);
const [ selectedIlce, setSelectedIlce ] = useState(null);
let queryTask;
let query;
let sfs;
let lineSymbol;
let polygon;
let polyline;
let graphic;
let extent;
let point;
let wMercatorUtils;
let rfConverter;
loadModules([
'esri/tasks/query',
'esri/tasks/QueryTask',
'esri/symbols/SimpleFillSymbol',
'esri/symbols/SimpleLineSymbol',
'esri/geometry/Polygon',
'esri/geometry/Polyline',
'esri/geometry/webMercatorUtils',
'esri/geometry/Extent',
'esri/geometry/Point',
'esri/graphic',
'esri/Color',
'libs/ReferenceConverter'
]).then(
(
[
Query,
QueryTask,
SimpleFillSymbol,
SimpleLineSymbol,
Polygon,
Polyline,
webMercatorUtils,
Extent,
Point,
Graphic,
Color,
referenceConverter
]
) => {
queryTask = QueryTask;
query = Query;
polygon = Polygon;
polyline = Polyline;
graphic = Graphic;
extent = Extent;
point = Point;
wMercatorUtils = webMercatorUtils;
rfConverter = referenceConverter;
sfs = new SimpleFillSymbol(
SimpleFillSymbol.STYLE_SOLID,
new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([ 0, 255, 255 ]), 4),
new Color([ 140, 140, 140, 0.25 ])
);
lineSymbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([ 0, 255, 255 ]), 4).setWidth(4);
}
);
const getAdres = async (url) => {
let response = await fetch(url);
let data = await response.json();
let list = [];
data.AdresList.Adresler.Adres.forEach((item) => {
list.push({
label: item.ADI,
value: item.ID,
lat: item.LAT,
lon: item.LON
});
});
return list;
};
async function ilceLoad() {
let ilceList = await getAdres(ilceUrl);
setIlceler(ilceList);
}
const convertExtent = (lat, lon) => {
let p;
let ext;
const sr = props.map.spatialReference;
if (sr.wkid == 102100) {
const _p = wMercatorUtils.lngLatToXY(lon, lat);
ext = extent({
xmin: _p[0],
ymin: _p[1],
xmax: _p[0],
ymax: _p[1],
spatialReference: props.map.spatialReference
});
} else {
const res = rfConverter.WgsToItrf(lat, lon);
ext = extent({
xmin: res.x,
ymin: res.y,
xmax: res.x,
ymax: res.y,
spatialReference: props.map.spatialReference
});
p = point(res.x, res.y);
}
p.spatialReference = sr;
return ext;
};
const ilceZoom = (evt) => {
setSelectedIlce(evt.value);
console.log('selectedIlce', selectedIlce);
setError(false);
console.log('error', error);
const qTask = queryTask(maksIlce);
const q = query();
q.returnGeometry = true;
q.outFields = [ '*' ];
q.outSpatialReference = { wkid: 5254 };
q.where = `KIMLIKNO=${evt.value}`;
qTask.execute(q, (evt) => {
const polyGon = polygon({
rings: evt.features[0].geometry.rings
});
props.map.graphics.add(graphic(polyGon, sfs));
});
const extent = convertExtent(evt.lat, evt.lon);
props.map.setExtent(extent);
mahalleLoad();
};
return (
<Select name='adresSelect' options={ilceler} onChange={(e) => ilceZoom(e)} placeholder='İlçe Seçiniz' />
);
};
const mapStateToProps = (state) => ({
map: state.map.map
});
export default connect(mapStateToProps, null)(AddressSearchMaks);
It can be related for some environment binding issue. Try to use the the setState as function:
useEffect(() => {
ilceLoad();
}, []);
const [ ilceler, setIlceler ] = useState([]);
const [ selectedIlce, setSelectedIlce ] = useState(null);
async function ilceLoad() {
let ilceList = await getAdres(ilceUrl);
setIlceler(ilceList); // update the state, it works here
}
const ilceZoom = (evt) => {
setSelectedIlce(prev => {
console.log("prev: ", prev);
console.log("evt.value: ", evt.value);
return evt.value;
});
const qTask = queryTask(url);
const q = query();
q.returnGeometry = true;
q.outFields = [ '*' ];
q.outSpatialReference = { wkid: 5254 };
q.where = `VARIABLE NAME=${evt.value}`;
qTask.execute(q, (evt) => {
const polyGon = polygon({
rings: evt.features[0].geometry.rings
});
props.map.graphics.add(graphic(polyGon, sfs));
});
const extent = convertExtent(evt.lat, evt.lon);
props.map.setExtent(extent);
};
Can you try like this. Because, in your code, you setting the data in selectedIlce, but before it re-render, your trying to checking the value in the console, so better use your console outside the event function, so that when it get updated, it will reflect in the console.
console.log('selectedIlce', selectedIlce);
const ilceZoom = (evt) => {
setSelectedIlce(evt.value);
....
}

Resources