can someone see why do I have selectionNames:[] empty when I console log ??
In the rest of my code, I'm using only selectionNames, it has the same content of names, but with an extra field selected. Therefore, I've thought using one sate i.e names instead of 2 i.e selectionNames and modifying names directly in my useEffect (to add my selected field but it's not working.
Can someone see where is the issue please ?
export default function Display() {
const [names, setNames] = useState([])
useEffect(() => {
axios.post("")
.then(res => {
console.log(res)
setNames(res.data.names)
})
.catch(err => {
console.log(err)
})
}, []);
const init = (e) => {
return e.map((item) => {
return {..item,types: item.types.map((t) => ({ ...t, selected: true }))
};
});
};
const [selectionNames, setSelectionNames] = useState(init(names));
console.log(selectionNames)
...
const change = (id,item, value) => {setSelectionStandards((s) => s.map((item) => {...} return item;}));
};
return (
<>
{selectionNames.map((item) => (...))}
</>
);
}
Here is my json from my api:
{
"names": [
{
"id": 1,
"Description": "descr",
"types": [
{
"id": 1,
"decription":"descr1",
},
...
]
},
...
]
}
This is what you should do:
import {useState, useEffect} from 'react';
const Display = () => {
// The initial state is empty
const [names, setNames] = useState([]);
// This function will be called when component mounts
const init = async () => {
const {data} = axios.post('');
setNames(data.names);
}
useEffect(() => {
init();
} , [])
// First time, this will print an empty array.
// After the initialization, you will get the actual names array
console.log(names);
}
import {useState, useEffect} from 'react';
const Display = () => {
// The initial state is empty
const [names, setNames] = useState([]);
// This function will be called when component mounts
const init = async () => {
const { data } = await axios.post(''); // put the await before, call axios
setNames(data.names);
}
useEffect(() => {
init();
} , [])
// First time, this will print an empty array.
// After the initialization, you will get the actual names array
console.log(names);
}
Related
I want to get a list of churches from the store at initialization but i can't. The log get my initial array and the new one but doesn't display. Log below:
log
here is my model:
const churchModel = {
items: [],
// ACTIONS
setAllChurches: action((state, payload) => {
state.items = payload;
}),
getInitialChurches: thunk(async (actions) => {
const { data } = await axios.post(
'http://localhost:3000/api/geo/closeto?latlong=2.3522219 48.856614&distance=10000'
);
let array = [];
const resData = data.map(async (index) => {
const res = await axios.get(`http://localhost:3000/api/institutions/all?idInstitution=${index.idInstitution}`);
array.push(res.data[0]);
});
actions.setAllChurches(array);
})
}
and my component:
const ChurchList = () => {
const classes = useStyles();
const setInitialChurches = useStoreActions(action => action.churches.getInitialChurches);
const churches = useStoreState(state => state.churches.items);
const [activeItem, setActiveItem] = React.useState(null);
useEffect(() => {
setInitialChurches()
}, []);
return (
<div className={classes.root} style={{marginTop: '20px',}}>
{ churches.map( (church) => (
<ChurchItem
key={ church.idInstitution }
church={ church }
setActiveItem={setActiveItem}
activeItem={activeItem}
/>)
), console.log(churches)}
</div>
)
};
export default ChurchList;
I tried a useEffect but nothing true. Could you help me please ?
that is not a good location to put console.log in, either put it outside the component render, inside the map or on a useEffect.
You can achieve it by using useEffect and passing churches on the array.
useEffect(() => {
// this will log everytime churches changes / initialized churches
console.log(churches);
}, [churches]);
Before posting the below, I have reviewed similar posts on stackoverflow but none resolved my issue.
I'm new to react and fetching data from firestore database. The below code works as required but getting this prompt within react
import React, {useState, useEffect} from 'react'
import {db} from '../firebase'
const ListRecord = () => {
const [details, setDetails] = useState([]);
useEffect(() => {
getContacts()
},[]);
const getContacts = async() => {
await db.collection('contacts').get().then((querySnapshot) => {
let arr = []
querySnapshot.forEach((doc) => {
arr.push({id: doc.id, value: doc.data()})
});
setDetails(arr);
});
console.log(details);
return details
}
return (
<div>
<h2>List Contact Details</h2>
</div>
)
}
export default ListRecord
As per other similar posts I tried moving the getContacts function inside useEffect body which make the prompt disapper but the getContacts function goes in a continuous loop.
I'm not sure what I'm missing here and any help would be appreciated.
There are different potential solutions:
1. Move getContacts() inside the useEffect() hook:
If you call getContacts() only once and only when the component mounts for the first time, this is probably the most logic solution.
useEffect(() => {
const getContacts = async () => {
await db.collection('contacts').get().then((querySnapshot) => {
let arr = []
querySnapshot.forEach((doc) => {
arr.push({
id: doc.id,
value: doc.data()
})
});
setDetails(arr);
});
//console.log(details);
//return details // why are you returning details?
}
getContacts()
}, [setDetails]); // setDetails() is granted to never change therefore the hook will never re-run
or, of course, you can use an IIFE:
useEffect(() => {
(async function() {
// ... same body as getContacts
})()
}, [setDetails])
2. Use a useCallback() hook:
This is something you might want to do if getContacts() is called more than once (for example, when the component mounts and every time some prop changes or when you click on some button)
const getContacts = useCallback(async () => {
await db.collection('contacts').get().then((querySnapshot) => {
let arr = []
querySnapshot.forEach((doc) => {
arr.push({
id: doc.id,
value: doc.data()
})
});
setDetails(arr);
});
//console.log(details);
//return details // why are you returning details?
}, [setDetail]); // setDetails() is granted to never change therefore getContacts() will never be re-created
useEffect(() => {
getContacts()
}, [getContacts]); // as getContacts() never changes, this will run only once
3. Move getContacts() out of the component and make it an independent function:
This can make sense if you want to reuse the same logic into other components:
// getContacts.js file
// OR this code could be in the ListRecord.js file but **outside** the component,
// although, in this case, solutions (1) or (2) would make more sense
import { db } from 'path/to/firebase'
export async function getContacts() {
await db.collection('contacts').get().then((querySnapshot) => {
let arr = []
querySnapshot.forEach((doc) => {
arr.push({
id: doc.id,
value: doc.data()
})
});
return arr; // this time you HAVE TO return arr
});
}
// ListRecord.js file
import React, { useState, useEffect } from 'react';
import { getContacts } from 'path/to/getContacts.js';
const ListRecord = () => {
const [details, setDetails] = useState([]);
useEffect(async () => {
const arr = await getContacts();
if (arr && arr.length > 0) setDetails(arr);
}, [setDetails]);
//...
}
I suggest you have a look at how useEffect and its dependency list works in the official document.
In short, do the following:
useEffect(() => {
getContacts()
}, [getContacts]);
This means when getContacts changes, the useEffect will be re-run.
Following is the data useEffect is returning in console log:
{
"sql": {
"external": false,
"sql": [
"SELECT\n date_trunc('day', (\"line_items\".created_at::timestamptz AT TIME ZONE 'UTC')) \"line_items__created_at_day\", count(\"line_items\".id) \"line_items__count\"\n FROM\n public.line_items AS \"line_items\"\n GROUP BY 1 ORDER BY 1 ASC LIMIT 10000",
[]
],
"timeDimensionAlias": "line_items__created_at_day",
"timeDimensionField": "LineItems.createdAt",
"order": {
"LineItems.createdAt": "asc"
}
I want to be able to render the above in my react app.
const ChartRenderer = ({ vizState }) => {
let ur = encodeURIComponent(JSON.stringify(vizState.query));
let u = "http://localhost:4000/cubejs-api/v1/sql?query=" + ur;
console.log(u)
useEffect(() => {
if(u !=="http://localhost:4000/cubejs-api/v1/sql?query=undefined") {
fetch(u)
.then(response => (response.json()))
.then(data => console.log(JSON.stringify(data, null, 4)))
}},[u]);
const { query, chartType, pivotConfig } = vizState;
const component = TypeToMemoChartComponent[chartType];
const renderProps = useCubeQuery(query);
return component && renderChart(component)({ ...renderProps, pivotConfig })
};
You would have to use a state variable to persist data and update it whenever the API returns some data. In a functional component, you can use the useState hook for this purpose.
const ChartRenderer = ({ vizState }) => {
// useState takes in an initial state value, you can keep it {} or null as per your use-case.
const [response, setResponse] = useState({});
let ur = encodeURIComponent(JSON.stringify(vizState.query));
let u = "http://localhost:4000/cubejs-api/v1/sql?query=" + ur;
console.log(u)
useEffect(() => {
if(u !=="http://localhost:4000/cubejs-api/v1/sql?query=undefined") {
fetch(u)
.then(response => (response.json()))
.then(data => {
// You can modify the data here before setting it to state.
setResponse(data);
})
}},[u]);
// Use 'response' here or pass it to renderChart
const { query, chartType, pivotConfig } = vizState;
const component = TypeToMemoChartComponent[chartType];
const renderProps = useCubeQuery(query);
return component && renderChart(component)({ ...renderProps, pivotConfig })
};
You can read more about useState in the official documentation here.
I have the following functional component where, on load of the component, it needs to loop through an array and run some async queries to populdate a new array I want to display in render method.
import React, { useEffect, useState, useContext } from 'react';
import { AccountContext } from '../../../../providers/AccountProvider';
import { GetRelationTableCount } from '../../../../api/GetData';
import { getTableAPI } from '../../../../api/tables';
const RelatedRecordsPanel = (props) => {
const { userTokenResult } = useContext(AccountContext);
const { dataItem } = props;
const [relatedTableItems, setRelatedTableItems] = useState([]);
useEffect(() => {
const tempArray = [];
const schema = `schema_${userTokenResult.zoneId}`;
const fetchData = async () => {
return Promise.all(
dataItem.tableinfo.columns
.filter((el) => el.uitype === 1)
.map(async (itm) => {
const tblinfo = await getTableAPI(itm.source.lookuptableid);
const tblCount = await GetRelationTableCount(
dataItem.tableid,
itm.source.jointable,
schema,
);
const TableIconInfo = { name: tblinfo.name, icon: tblinfo.icon, count: tblCount };
tempArray.push(TableIconInfo);
})
);
};
fetchData();
setRelatedTableItems(tempArray)
}, []);
return (
<div>
{relatedTableItems.length > 0 ? <div>{relatedTableItems.name}</div> : null}
</div>
);
};
In the above code, the queries run correctly and if I do a console.log in the loop, I can see if fetches the data fine, however, the array is always [] and no data renders. How do I write this async code such that it completes the queries to populate the array, so that I can render properly?
Thx!
You aren't using the return value of the Promise.all and since all your APIs are async, the tempArray is not populated by the time you want to set it into state
You can update it like below by waiting on the Promise.all result and then using the response
useEffect(() => {
const schema = `schema_${userTokenResult.zoneId}`;
const fetchData = async () => {
return Promise.all(
dataItem.tableinfo.columns
.filter((el) => el.uitype === 1)
.map(async (itm) => {
const tblinfo = await getTableAPI(itm.source.lookuptableid);
const tblCount = await GetRelationTableCount(
dataItem.tableid,
itm.source.jointable,
schema,
);
const TableIconInfo = { name: tblinfo.name, icon: tblinfo.icon, count: tblCount };
return TableIconInfo;
})
);
};
fetchData().then((res) => {
setRelatedTableItems(res);
});
}, []);
as show below, I pass a parameter list to an useFunction; In useGetName function, I need control params "data" as "item" for some reason。It turns out that circulate update。
import React, { useState, useEffect } from "react";
function App() {
const [filter, setFilter] = useState([]);
useEffect(() => {
const id = setTimeout(() => {
setFilter([{ value: "" }]);
}, 1000);
return () => {
clearTimeout(id);
};
}, []);
const list = [
{
name: "a",
value: filter
}
];
return <div>{useGetName(list)}</div>;
}
function useGetName(data) {
const [item, setItem] = useState(data);
useEffect(() => {
// for some reason, I need control params "data" as "item"
console.log("data", data);
setItem(data);
}, [data]);
return (
<div
onClick={() => {
console.log("item", item);
}}
>
click me
</div>
);
}
export default App;
the way I have fixed like that:
useEffect(() => {
console.log("data", data);
setItem(data);
}, [JSON.stringify(data)]); // JSON.stringify
how to fix it? thanks