How can I use fetched const value in react? - javascript

const fetchStocks = () => {
const API_KEY = "dddd";
const StockSymbol = 'IBM';
const API_Call = `https://www.alphavantage.co/query?function=OVERVIEW&symbol=${StockSymbol}&apikey=${API_KEY}`;
const StockSymbols = [];
const StockName = [];
const StockIndustry = [];
fetch(API_Call)
.then(
function (response) {
return response.json();
}
)
.then(
function (data) {
console.log(data);
StockSymbols.push(data['Symbol']);
StockName.push(data['Name']);
StockIndustry.push(data['Industry']);
console.log(StockName)
console.log(StockSymbols)
console.log(StockIndustry)
})
// const table = {
// columns: [
// { headername: "Symbol", field: "symbol" },
// { headername: "Name", field: "name" },
// { headername: "Industry", field: "industry" }
// ],
// rowData: [
// { symbol: StockSymbols, name: StockName, industry: StockIndustry }
// ]
// }
return (
<div>
{StockSymbols}, {StockName}, {StockIndustry}
</div>
)
};
export default fetchStocks;
I'm trying to get stock name, symbol, and industry using Alpha Vantage.
And when I console.log them, it seems like it's working.
But when I try to use them in the return part with {StockSymbols}, {StockName}, {StockIndustry},
nothing passes and nothing shows up.
What do I have to do to use them?

Hope this is what you are looking for, as your question is not self explanatory enough.
Create a component state
const [data, setData] = useState({StockName: null, StockSymbols: null, StockIndustry: null})
Move fetch(API_Call) in useEffect hook. (componentDidMount incase not using hooks)
useEffect(() => {
fetch(API_Call)
.then(
function (response) {
return response.json();
}
)
.then(
function (data) {
setData({
StockName: data['Symbol'],
StockSymbols: data['Name'],
StockIndustry: data['Industry']
})
})
}, [])
Use component state in JSX
return (<>
<p>{data.StockName}</p>
<p>{data.StockSymbols}</p>
<p>{data.StockIndustry}</p>
</>

If you're aiming to:
fetch the data on mount of the React component
then show it in the UI
you'd need to keep some state that re-renders the component. So for you that probably means initiating the array as const [StockIndustry, setStockIndustry] = useState([]); and setStockIndustry([...StockIndustry, data['Symbol']]; in your life cycle method/hook. Simply pushing to the array will not re-render the component.

Related

How can I turn this nested if function into functional style programming with Ramda in Javascript

I was looking at the code in Prisma for softdelete middleware:
prisma.$use(async (params, next) => {
// Check incoming query type
if (params.model == 'Post') {
if (params.action == 'delete') {
// Delete queries
// Change action to an update
params.action = 'update'
params.args['data'] = { deleted: true }
}
if (params.action == 'deleteMany') {
// Delete many queries
params.action = 'updateMany'
if (params.args.data != undefined) {
params.args.data['deleted'] = true
} else {
params.args['data'] = { deleted: true }
}
}
}
return next(params)
})
The steps it takes is:
Check if Post -> if it's a 'delete' or 'deleteMany' then change action to 'update' and set deleted to true.
The nested if's seems not ideal and it seemed that this would be cleaner using a functional style, so I used Ramda to try and make it functional:
const model = lensProp('model')
const action = lensProp('action')
const argsData = compose(lensProp('args'), lensProp('data'))
const cView = curry(view)
const modelView = cView(model)
const actionView = cView(action)
const _shouldBeModified = (models, actions, p) => and(
includes(modelView(p), models),
includes(actionView(p), actions)
)
const shouldBeModified = curry(_shouldBeModified)
const timeElapsed = Date.now();
const today = new Date(timeElapsed);
const softDelete = (models, actions, params) => ifElse(
shouldBeModified(models, actions),
pipe(
set(argsData, { deleted: true, deletedAt: today.toISOString()}),
set(action, 'update')
),
identity
)(params)
I can then call it like this:
softDelete(['Post'],['delete', 'deleteMany'],params)
/*
returns: {"action": "update", "args": {"data": {"deleted": true, "deletedAt": "2022-04-17T19:49:00.294Z"}}, "model": "Post"}
*/
All of that said, I'm new to Ramda and it seems like my approach is messy, can someone help me clean this up?
You can use R.when with R.where to check if the params should be updated. If so, use R.evolve to update them.
Pass an actions object, instead of an array, so the code can map the update / updateMany according to the original action.
const { curry, when, where, includes, __, keys, evolve, prop, mergeDeepLeft } = R
const actionsMap = { delete: 'update', deleteMany: 'updateMany' };
const softDelete = curry((models, actions, params) => when(
where({
model: includes(__, models),
action: includes(__, keys(actions))
}),
evolve({
action: prop(__, actions),
args: mergeDeepLeft({ data: { deleted: true, deletedAt: new Date().toISOString() }})
})
)(params))
const params = {"action": "deleteMany", "args": { data: { something: 5 } }, "model": "Post"}
const result = softDelete(['Post'], actionsMap, params)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js" integrity="sha512-t0vPcE8ynwIFovsylwUuLPIbdhDj6fav2prN9fEu/VYBupsmrmk9x43Hvnt+Mgn2h5YPSJOk7PMo9zIeGedD1A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

Trying to export data to csv file from my firebase data gives undefined

I have a list of data from my firebase firestore that I want to export to .csv
I did everything that is required but when I add the values that I want to be exported they are always undefined.
I am not an expert in react I am somewhat intermediate but I think it is because I am setting my data inside a useEffect Hook.
My data useState is undefined, although it holds values and I can see them in my table, which is causing the CSVLink to throw errors.
How do I allow my data to be passed into the headers?
Here is my code:
const [data, setData] = useState([]);
const [id, setID] = useState("");
const list = []
const filteredList = []
useEffect(() => {
firebase.firestore().collection("Users").get().then((userSnapshot) => {
userSnapshot.forEach((doc) => {
const {powerAccount,first_name,registerDate,email,company,country,phone} = doc.data();
setID(doc.data().usersID)
list.push({
usersID:doc.id,
powerAccount:powerAccount,
first_name:first_name,
registerDate:registerDate,
email:email,
company:company,
country:country,
phone:phone,
});
});
setData(list);
});
},[]);
const headers = [
// here all the keys give undefined.
{label:'User',key:data.usersID},
{label:'Account',key:data.powerAccount},
{label:'Name',key:data.first_name},
{label:'RegistrationDate',key:data.registerDate},
{label:'Email',key:data.email},
{label:'Company',key:data.company},
{label:'Country',key:data.country},
{label:'Phone',key:data.phone},
];
const csvReport = {
filename:"userReport.csv",
headers:headers,
data: data // also my data useState is undefined, although it holds values and i can see them in my table
}
return (
<CSVLink {...csvReport} >
Export
</CSVLink>
)
According to your implementation, fetching data from firebase is async so the csvData is getting undefined because it's not updating after a state update
Try changing your code like this and let me know if it works fine
const [data, setData] = useState({
filename: "userReport.csv",
headers: [],
data: [],
});
const [id, setID] = useState("");
const filteredList = [];
useEffect(() => {
firebase
.firestore()
.collection("Users")
.get()
.then((userSnapshot) => {
let list = [];
userSnapshot.forEach((doc) => {
const {
powerAccount,
first_name,
registerDate,
email,
company,
country,
phone,
} = doc.data();
setID(doc.data().usersID);
list.push({
usersID: doc.id,
powerAccount: powerAccount,
first_name: first_name,
registerDate: registerDate,
email: email,
company: company,
country: country,
phone: phone,
});
});
const headers = [
// I'm not sure why you need this key
// but if it's only for uniqueness
// you can replace them by unique strings like
// { label: "User", key: "user" },
// { label: "Account", key: "account" },
{ label: "User", key: data.usersID },
{ label: "Account", key: data.powerAccount },
{ label: "Name", key: data.first_name },
{ label: "RegistrationDate", key: data.registerDate },
{ label: "Email", key: data.email },
{ label: "Company", key: data.company },
{ label: "Country", key: data.country },
{ label: "Phone", key: data.phone },
];
const csvReport = {
filename: "userReport.csv",
headers: headers,
data: list,
};
setData(csvReport);
});
}, []);
return <CSVLink {...data}>Export</CSVLink>;
You should all state coordination / update to useState and useEffect hooks and avoid relying on any field update outside the scope of these.
You should then remove the list variable, move state update to your effect hook and consolidate all users data in the same structure:
const [data, setData] = useState([]);
useEffect(() => {
firebase.firestore()
.collection("Users")
.get()
.then((userSnapshot) => {
const usersData = [];
userSnapshot.forEach((doc) => {
const { powerAccount, first_name, registerDate, email, company, country, phone, userID } = doc.data();
const userData = {
usersID: doc.id,
powerAccount: powerAccount,
first_name: first_name,
registerDate: registerDate,
email: email,
company: company,
country: country,
phone: phone,
};
const headers = [
// here all the keys give undefined.
{ label: 'User', key: userID },
{ label: 'Account', key: powerAccount },
{ label: 'Name', key: first_name },
{ label: 'RegistrationDate', key: registerDate },
{ label: 'Email', key: email },
{ label: 'Company', key: company },
{ label: 'Country', key: country },
{ label: 'Phone', key: phone },
];
const csvReport = {
filename: "userReport.csv",
headers: headers,
data: userData
}
usersData.push(csvReport);
});
setData(usersData);
});
}, []);
return (
<CSVLink {...data} >
Export
</CSVLink>
)
You may need add loading state to reflect the UI effect of data being loaded.
I think there are two things that causes the problem that you need to understand.
Asynchronous Function
React Lifecycle
Fetching data from firebase is asynchronous and might take sometime before you get the returned data while you have saved csvReport as constant variables and set it up as React element properties. So when firebase is still loading your data and your react component is already rendered / mounted, your data state has value of [] from default value as defined in the useState statement. Based on your code, your csvReport constant variable will not be receiving new data from firebase unless your app is re-rendered (enter new lifecycle and repeat). For example, switching to other tab component and go back to this component without refreshing the browser.
const csvReport = {
filename:"userReport.csv",
headers:headers, => [{ label: "User", key: undefined }, ...etc]; undefined bcs `data` is []
data: data => the value is []
}
So the simple solution is NOT to save the data as constant variable and set up the React element properties directly from your useState variable. Based on your code, I would make some changes like this.
...your previous code
const getHeaders = () => {
// Do your data manipulation using `data` in useState
// For example:
const headers = data && data.map(item => {return {id: item.id}})
return headers
}
return (
<CSVLink
filename="userReport.csv"
headers={getHeaders()}
data={data}
>
Export
</CSVLink>
)
Hope this helps and have fun making changes :)

React: rendering an Array of Data

Working on my first react project and not making any progress on how to loop through and render my data to the front-end.
I am using axios to get a list of stock tickers from a MySQL database, and for each of those i am once again using axios to scrape some values from an external website.
See code snippet below:
const fetchStocks = () => {
let stocksList = []
Axios.get('http://localhost:3001/fetchStocks').then((response) => {
response.data.map((val, key) => {
const url = 'https://www.tradegate.de/orderbuch.php?isin=' + val.stockTicker
Axios.get(url).then((response) => {
let $ = cheerio.load(response.data)
let name = $('#col1_content h2')[0].children[0].data
let last = $('#last')[0].children[0].data
let delta = $('#delta')[0].children[0].data
let high = $('#high')[0].children[0].data
let low = $('#low')[0].children[0].data
stocksList.push({
sId: val.sId,
stockName: name,
stockTicker: val.stockTicker,
stockLast: last,
stockDelta: delta,
stockHigh: high,
stockLow: low
})
})
})
})
}
When i do console.log(stocksList) i pretty much get what i want:
[
{
"sId": 3,
"stockName": "Tesla Inc.",
"stockTicker": "US88160R1014",
"stockLast": "1 049,80",
"stockDelta": "+8,90%",
"stockHigh": "1 049,80",
"stockLow": "966,90"
},
{
"sId": 1,
"stockName": "Apple Inc.",
"stockTicker": "US0378331005",
"stockLast": "128,00",
"stockDelta": "-1,16%",
"stockHigh": "130,28",
"stockLow": "127,70"
},
{
"sId": 2,
"stockName": "Intel Corp.",
"stockTicker": "US4581401001",
"stockLast": "42,725",
"stockDelta": "+0,78%",
"stockHigh": "42,85",
"stockLow": "42,37"
}
]
I would now like to do something like:
{
stocksList.map(stock => {
return (
<li key = {stock.sId}>{stock.stockName}</li>
)
})
}
for each entry in the database, but so far i had no luck and don't quite understand where i'm wrong - im guessing something about how i'm setting up the array?
Since your are returning JSX you may want to use () braces instead of {}.
{
stocksList.map(stock => (
return (
<li key = {stock.sId}>{stock.stockName}</li>
)
))
}
Or simply:
{
stocksList.map(stock => <li key = {stock.sId}>{stock.stockName}</li>)
}
P.S. also I don't see a part of your code where you are trying to render an array
When the component did mount, useEffect will be called then it calls fetchStocks function, finally fetch data from the server and puts at a component state via useState
import React, { useState, useEffect } from 'react';
const FC = () => {
const [stocksList, setStocksList] = useState([])
const fetchStocks = () => {
let list = []
Axios.get('http://localhost:3001/fetchStocks').then((response) => {
response.data.map((val, key) => {
const url = 'https://www.tradegate.de/orderbuch.php?isin=' + val.stockTicker
Axios.get(url).then((response) => {
let $ = cheerio.load(response.data)
let name = $('#col1_content h2')[0].children[0].data
let last = $('#last')[0].children[0].data
let delta = $('#delta')[0].children[0].data
let high = $('#high')[0].children[0].data
let low = $('#low')[0].children[0].data
list.push({
sId: val.sId,
stockName: name,
stockTicker: val.stockTicker,
stockLast: last,
stockDelta: delta,
stockHigh: high,
stockLow: low
})
})
})
})
setStocksList(list)
}
useEffect(() => {
fetchStocks()
}, [])
return stocksList.map(stock => <li key = {stock.sId}>{stock.stockName}</li>)
}

React hook useEffect runs continuously forever/infinite loop, making duplicate of data fetching from APi,

im fetching data from APi and doing filtering from severside, i'm using useInfiniteScroll to fetch only limited amount of data on the first page, and with this im doing pagination too...
const [casesList, setCasesList] = useState<CaseModel[]>([]);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [isFetchingData, setIsFetchingData] = useState<boolean>(true);
const { inputValue } = React.useContext(MenuContext);
const debouncedValue = useDebounce(inputValue, 10);
these are my hook, (casesList) in which im saving all my incoming data from APi, input value is the value that im typing in search box for filtering the data, and debouncedValue is my custom hook, so the inputValue first goes to debouncedValue and then my debouncedValue will get the value of my inputValue,
const [pagination, setPagination] = useState<Pagination>({
continuationToken: "",
hasMoreResults: true,
});
const [isFetchingMore, setIsFetchingMore] = useInfiniteScroll();
these are my pagination and useInfiniteScroll() hooks...
so the actual problem that i'm facing is that,
const getDashboardCases = useCallback(
async (continuationToken: string) => {
setIsLoading(true);
let casesPageLimit = CASES_PAGE_LIMIT;
if (casesList.length === 0) {
const table = document.querySelector("#cases-items");
if (table) {
const caseItemHeight = 80;
const heightDifference =
table?.getBoundingClientRect().y > 0
? window.innerHeight - table?.getBoundingClientRect().y
: -1;
casesPageLimit = Math.max(
casesPageLimit,
Math.ceil(heightDifference / caseItemHeight)
);
}
}
const options: GetCasesListOptions = {
continuationToken,
filter: [],
sort: [],
pageLimit: casesPageLimit,
search: [debouncedValue]
};
const data: IData = await dashboardService.getCasesList(options);
setIsFetchingMore(false);
setIsLoading(false);
if (data.result) {
setIsFetchingData(false)
if (data.continuationToken !== undefined) {
const newContinuationToken = data.continuationToken;
setPagination((prevPagination) => ({
...prevPagination,
hasMoreResults: data.hasMoreResults,
continuationToken: newContinuationToken,
}));
} else {
setPagination((prevPagination) => ({
...prevPagination,
hasMoreResults: false,
}));
}
setCasesList((prevCases) => [...prevCases, ...data.result]);
dispatch(setAllowedClassifications(data.options));
}
},
[casesList.length, dashboardService, debouncedValue]
);
this code is fetching the data from the APi and for filtering i created an Object name Options
const options: GetCasesListOptions = {
continuationToken,
filter: [],
sort: [],
pageLimit: casesPageLimit,
search: [debouncedValue]
};
im saving my debouncedValue to the search Array in the Options object and then im using Options object in APi to filter the data
const data: IData = await dashboardService.getCasesList(options);
for example if i have 15 objects in APi, i need to get first 10 objects, and then i scroll down my callback function executes one more time and get the rest of the data...
if (data.result) {
setIsFetchingData(false)
if (data.continuationToken !== undefined) {
const newContinuationToken = data.continuationToken;
setPagination((prevPagination) => ({
...prevPagination,
hasMoreResults: data.hasMoreResults,
continuationToken: newContinuationToken,
}));
} else {
setPagination((prevPagination) => ({
...prevPagination,
hasMoreResults: false,
}));
}
setCasesList((prevCases) => [...prevCases, ...data.result]);
dispatch(setAllowedClassifications(data.options));
}
it's already done there...
now i want that, if i type something in the search box my api should run again to add my search value in the APi and filters the data...
but i'm facing problems doing this...
im calling my usecallback function like this...
useEffect(() => {
if (isFetchingMore && pagination.hasMoreResults) {
getDashboardCases(pagination.continuationToken);
}
}, [
getDashboardCases,
isFetchingMore,
pagination.continuationToken,
pagination.hasMoreResults,
debouncedValue
]);
if isFetchingMore && pagination.hasMoreResults is true, then it executes the function, but if type something in searchbox it is not running my function again...
i also tried to remove the if condition in the useEffect but it started infinite scrolling making duplicates of data, and i get this error...
Encountered two children with the same key, `d77c39f2-2dcd-4c4e-b7ee-1fde07b6583f`. Keys should be unique so that components maintain their identity across updates
so i need to re-run the function if i type something in search box and not get duplicated data back, and also i want to run the if condition that i typed in the useEffect...
please help, Thank you :)

checkbox not showing at view time in datagrid of material-ui

Some main code related to datagrid
const apiRef = React.useRef(null);
const [gotApiRef, setGotApiRef] = useState(false);
const [gotApiRef2, setGotApiRef2] = useState(false);
console.log("apiRef.current: ", apiRef.current);
const ObjRef = React.useRef({
dataRe: {
columns: [],
rows: [],
},
tempRe: {
rows: [],
},
});
const columns = [
{ field: "lenderName", headerName: "Lender Name", width: 200 },
{ field: "endpoint", headerName: "Endpoint", width: 250 },
];
// filling row data with props
useEffect(() => {
if (props.data.docs) {
props.data.docs.map((row) => {
let temp = {
id: row._id,
lenderName: row.name,
endpoint: row.endpoint,
};
ObjRef.current.tempRe.rows.push(temp);
});
console.log("2. ObjRef.current.tempRe.rows:", ObjRef.current.tempRe.rows);
}
}, [props.data]);
// ====================== Checkbox Selection useEffect =========================
useEffect(() => {
if (gotApiRef) {
console.log("entry in useEffect");
//console.log("1. ObjRef.current.dataRe: ", ObjRef.current.dataRe);
console.log("In useEffect apiRef.current: ", apiRef.current);
const dataRe = {
columns,
rows: ObjRef.current.tempRe.rows,
};
counterRef.current.renderCount += 1;
// console.log(
// "3 counterRef.current.renderCount:",
// counterRef.current.renderCount
// );
ObjRef.current.dataRe = dataRe;
const rowModels = apiRef?.current?.getRowModels();
if (rowModels != undefined) {
console.log("5 ObjRef.current.dataRe:", ObjRef.current.dataRe);
console.log("in rowModel not undefined..");
console.log("5. rowModels*:", rowModels);
if (apiRef.current) {
console.log("in apiRef.current*", apiRef.current);
setGotApiRef2(true)
apiRef.current.setRowModels(
rowModels.map((r) => {
console.log("in rowModels.map");
r.selected = true;
return r;
})
);
}
}
}
}, [gotApiRef]);
//in return
<DataGrid
rows={ObjRef.current.dataRe.rows}
columns={ObjRef.current.dataRe.columns}
// {...ObjRef.current.dataRe}
checkboxSelection
onSelectionChange={(newSelection) => {
setSelection(newSelection.rows);
}}
components={{
noRowsOverlay: (params) => {
console.log(
"in noRowOverlay ObjRef.current.dataRe:",
ObjRef.current.dataRe
);
console.log("params in noRowsOverlay**:", params);
if (!apiRef.current) {
apiRef.current = params.api.current;
console.log(
"apiRef.current in noRowOverlay:",
apiRef.current
);
setGotApiRef(true);
}
return <div>No rows</div>;
},
}}
/>
I read somewhere that use Layout Effect is used with use Ref, even tried that but no help
this question is work after this question with this name
"Can I initialize the checkbox selection in a material-ui DataGrid?"
Can I initialize the checkbox selection in a material-ui DataGrid?
I am getting data in apiRef.current
I am setting data in ObjRef.current.dataRe
after setting I should get data in rowModels ,but I am getting rowModels as null
this is solved, i just used a boolean state to make component render and removed dependency from useEffect.
upon some hit and trial it runs.

Categories

Resources