Fetching data with hook takes too long time - javascript

I am trying to fetch and map an array with 350 object's elements. I decided to use Hook and useEffect, to re render my dataTable component since mapping is done. Unfortunately, the whole process takes enormous amount of time, and it makes page unresponsive. After 1-2 minutes, table shows up and after few seconds it disappears. After that page is still unresponsive. Could someone explain why it happens, and give me some workaround? I would be grateful.
Code below:
const Employees = (props) => {
const [developers, setDevelopers] = useState([]);
useEffect(() => {
fetchData();
});
const columns = [
{
name: "Emloyee",
selector: "name",
sortable: true,
},
{
name: "Team ",
selector: "team",
sortable: true,
},
{
name: "Email ",
selector: "email",
sortable: true,
},
];
const fetchData = () => {
axios.get("http://localhost:3128/employees", {
headers: {
'Access-Control-Allow-Origin': '*',
}
})
.then((response) => {
mapData(response.data.developers);
console.log("I am here!");
})
.catch((e) => console.log(e));
};
const mapData = (jsonData) => {
jsonData.forEach((x) => {
let newDeveloper = {
name: x.userId,
team: x.team,
email: x.userId + "#mail.com",
};
setDevelopers((developers) => [...developers, newDeveloper]);
});
};
return <DataTable title="Employees" columns={columns} data={developers}/>;
};

useEffect without dependency array will run on every render, so in your case, you are stuck in an infinite loop which cause page to become unresponsive
solution:
const fetchData = useCallback(() => {
axios.get("http://localhost:3128/employees", {
headers: {
'Access-Control-Allow-Origin': '*',
}
})
.then((response) => {
mapData(response.data.developers);
console.log("I am here!");
})
.catch((e) => console.log(e));
},[]);
const mapData = useCallback((jsonData) => {
jsonData.forEach((x) => {
let newDeveloper = {
name: x.userId,
team: x.team,
email: x.userId + "#kuehne-nagel.com",
};
setDevelopers((developers) => [...developers, newDeveloper]);
});
},[]);
useEffect(() => {
fetchData();
},[fetchData]); // pass dependency array here in useEffect

Thanks to #DrewReese and #SarthakAggarwal , I've got a solution:
const Employees = (props) => {
const [developers, setDevelopers] = useState([]);
const columns = [
{
name: "Emloyee",
selector: "name",
sortable: true,
},
{
name: "Team ",
selector: "team",
sortable: true,
},
{
name: "Email ",
selector: "email",
sortable: true,
},
];
const fetchData = useCallback(() => {
axios.get("http://localhost:3128/employees", {
headers: {
'Access-Control-Allow-Origin': '*',
}
})
.then((response) => {
mapData(response.data.developers);
console.log("I am here!");
})
.catch((e) => console.log(e));
}, []);
const mapData = (jsonData) => {
let table = [];
jsonData.forEach((x) => {
let newDeveloper = {
name: x.userId,
team: x.team,
email: x.userId + "#mail.com",
};
table = [...table,newDeveloper];
//setDevelopers((developers) => [...developers, newDeveloper]);
});
setDevelopers((developers) => table);
};
useEffect(() => {
fetchData();
}, [fetchData]);
return <DataTable title="Employees" columns={columns} data={developers}/>;
};
Thanks a lot !

Related

React - State is not updating when it is supposed to, why is react doing this? (not retaining)

Hi so I'm trying to grab some json from an api and then populate a table, pretty simple stuff.
What's happening is that I can see the "tableData" state being updated as each new row comes in, I'm also logging every time "tableData" is updated, yet maybe .5 seconds after its all done my "tableData" is empty again (check console screenshots)
const [bigChartData, setbigChartData] = React.useState("data1");
const [tableData, setTableData] = React.useState([]);
const setBgChartData = (name) => {
setbigChartData(name);
};
const getData = () => {
axios.get("URL")
.then(res => {
const data = res.data.items.forEach(item => {
setTableData(oldData => [...oldData, {
data: [
{ text: item.title },
{ text: "asd" + item.url },
{ text: "some links..." }
]
}]);
});
})
.catch(err => console.log(err));
setTimeout(function () {
console.log(tableData);
}, 3000);
}
useEffect(() => {
getData();
}, []);
useEffect(() => {
console.log("Table data updated:");
console.log(tableData);
}, [tableData]);
I think you should not iterate through each row inside getData() method instead try following code
const getData = () => {
axios.get("URL")
.then(res => {
const data = res.data.items.map(item => {
return{
data: [
{ text: item.title },
{ text: "asd" + item.url },
{ text: "some links..." }
]
};
});
setTableData(data)
}).catch(err => console.log(err));
}
or if you have already some data in tableData then
setTableData([...tableData, data])

useEffect debounce search technique

i'm trying to implement search functionality but i don't want to call the api every time i type something . here is the code:
const [term, setTerm] = useState("");
const [result, setResult] = useState([]);
useEffect(() => {
const search = async () => {
const respond = await axios.get("https://en.wikipedia.org/w/api.php", {
params: {
action: "query",
list: "search",
origin: "*",
format: "json",
srsearch: term,
},
});
setResult(respond.data.query.search);
};
if (!result.length) {
if (term) {
search();
}
}
}, [term, result.length]);
you can use the setTimeout() function and clearTimeout in the cleanup function
and the useRef hook to get the previous state to call the API only once :
const [result, setResult] = useState([]);
const termUseRef = useRef();
useEffect(() => {
termUseRef.current = term
})
const prevTerm = termUseRef.current;
useEffect(() => {
const search = async () => {
const respond = await axios.get('https://en.wikipedia.org/w/api.php', {
params: {
action: 'query',
list: 'search',
origin: '*',
format: 'json',
srsearch: term,
},
});
setResult(respond.data.query.search);
};
if (!result.length) {
if (term) {
search();
}
} else if(term !== prevTerm) {
const debounceSearch = setTimeout(() => {
if (term) {
search();
}
}, 1200);
return () => {
clearTimeout(debounceSearch);
};
}
}, [term, result.length, prevTerm]);

How to extract Firebase data to variable after setState() with react?

First time using react and firebase real-time database. I'm having a hard time extracting the data and inserting it into const items. From my understanding, the firebase code is asynchronous, so the code will execute after the page is loaded up.
After checking the console numerous times, I see the data loads into const tasks array. However, the data inside tasks[] doesn't display when I put it inside the items array(title, subtitle, etc). I've looked at multiple solutions on this and none of them have answered my issue.
I thought it was because of the items being a const, but after changing it to var, the issue persisted. Any sort of guidance would be appreciated here.
One small note: using await doesn't seem to work for me whatsoever. I could be mixing things up due to my lack of knowledge of firebase + react.
const Timelines = () => {
const [tasks, setTasks] = useState([]);
useEffect(() => {
const ref = firebase.database().ref();
const listener = ref.on('value', snapshot => {
const fetchedTasks = [];
snapshot.forEach(childSnapshot => {
const key = childSnapshot.key; //using await here doesn't work. Return 'await is a reserved word error'
const data = childSnapshot.val(); //using await here doesn't work. Return 'await is a reserved word error'
fetchedTasks.push({ id: key, ...data });
});
setTasks(fetchedTasks);
});
return () => ref.off('value', listener);
}, [firebase.database()]);
const items = [{
title: "Header" + tasks,
cardTitle: "Title",
//url: "http://www.history.com",
cardSubtitle: "LA",
cardDetailedText: ["Paragrah"],
media: {
type: "IMAGE",
source: {
url: "./images/image1.jpeg"
}
}
},
{
title: "Title",
cardTitle: "Title1",
//url: "http://www.history.com",
cardSubtitle: "LA",
cardDetailedText: "Paragraph 1",
media: {
type: "IMAGE",
source: {
url: "./images/img-2.jpg"
}
}
},
]
return (
<div className='timeline' style={{ width: "auto", height: "auto" }}>
<Chrono items={items}
slideShow
mode="VERTICAL_ALTERNATING"
theme={{
primary: "#7e8083",
secondary: "white",
cardBgColor: "#00467f",
cardForeColor: "white",
titleColor: "#00467f"
}}
useReadMore="true" />
</div>
)
};
export default Timelines;
Your problem may be rerendering problem. So you can try loading state. Check my useEffect and return.
const Timelines = () => {
const [tasks, setTasks] = useState([]);
const [loading, setLoading] = useState(false); //<----
useEffect(() => {
setLoading(true) //<-----
const ref = firebase.database().ref();
const listener = ref.on('value', snapshot => {
const fetchedTasks = [];
snapshot.forEach(childSnapshot => {
const key = childSnapshot.key; //using await here doesn't work. Return 'await is a reserved word error'
const data = childSnapshot.val(); //using await here doesn't work. Return 'await is a reserved word error'
fetchedTasks.push({ id: key, ...data });
});
setTasks(fetchedTasks);
setLoading(false) //<---
});
return () => ref.off('value', listener);
}, [firebase.database()]);
const items = [{
title: "Header" + tasks,
cardTitle: "Title",
//url: "http://www.history.com",
cardSubtitle: "LA",
cardDetailedText: ["Paragrah"],
media: {
type: "IMAGE",
source: {
url: "./images/image1.jpeg"
}
}
},
{
title: "Title",
cardTitle: "Title1",
//url: "http://www.history.com",
cardSubtitle: "LA",
cardDetailedText: "Paragraph 1",
media: {
type: "IMAGE",
source: {
url: "./images/img-2.jpg"
}
}
},
]
return !loading && (
<div className='timeline' style={{ width: "auto", height: "auto" }}>
<Chrono items={items}
slideShow
mode="VERTICAL_ALTERNATING"
theme={{
primary: "#7e8083",
secondary: "white",
cardBgColor: "#00467f",
cardForeColor: "white",
titleColor: "#00467f"
}}
useReadMore="true" />
</div>
)
};
export default Timelines;
Thank you
What Gökhan Geyik stated as a solution is correct. But I had to adjust my code a little since my Firebase db structure was different. The problem was, rerendering!
useEffect(() => {
setLoading(true)
const ref = firebase.database().ref('Overview/events');
const listener = ref.on('value', snapshot => {
const fetchedTasks = [];
const data = snapshot.val();
fetchedTasks.push(data);
setTasks(fetchedTasks);
setLoading(false)
});
return () => ref.off('value', listener);
}, [firebase.database()]);
return !loading && (
....
)
This worked like a charm. Much kudos to Gökhan Geyik for the right direction!

useCallback function is not a function while using unit test Jest

This is my test code snippet but it throws an exception, TypeError: componentInstance.loadLoanApplication is not a function :
it('should render the SubmittedLoan', () => {
const loanData = {
data: {
id: 1,
};
const div = document.createElement('div');
const wrapper = mount(
<AppProviders>
<MemoryRouter initialEntries={['/review/153']}>
<SubmittedLoan
match={{ params: { loanId: 1, step: 1 } }}
history={{
location: { state: { from: 'register' } },
push() {},
}}
/>
</MemoryRouter>
</AppProviders>,
div,
);
const componentInstance = wrapper
.find(SubmittedLoan)
.children()
.first()
.children()
.first()
.instance();
const loanApplication = {
id: 1,
steps_data: [
{ slug: 'step_1', title: 'Step 1' },
{ slug: 'step_2', title: 'Step 2' },
],
status: ApiCaptiq.STATUS_SUBMITTED,
};
expect(wrapper.find(SubmittedLoan).length).toBe(1);
componentInstance.loadLoanApplication(1, 1);
componentInstance.onLoadLoanApplication(loanData);
componentInstance.onLoadFail();
componentInstance.setState({
formData: [{ item: 'value' }, { item2: 'value2' }],
activeStep: 1,
loanApplication,
});
componentInstance.handleSnackbarClose(new Event('click'), '');
componentInstance.setState({ activeStep: 3 });
});
Then my Component which uses memo is as follows :
export const SubmittedLoan = memo(() => {
const [loanApplication, setLoanApplication] = useState<LoanApplication | null>(null);
const [message, setMessage] = useState({
message: '',
open: false,
messageType: '',
});
const authContext = useContext(AuthContext);
const customerContext = useCustomerData();
const params = useParams();
const history = useHistory();
const classes = useStyles();
const { loanId } = params;
const onLoadFail = useCallback(() => {
setMessage({
message: 'Die verfügbaren Darlehensarten können nicht aufgelistet werden',
open: true,
messageType: 'error',
});
}, []);
const onLoadLoanApplication = useCallback(
(response: AxiosResponse) => {
setTemplateSettings(response, authContext);
if (
response.data.status === ApiCaptiq.STATUS_STARTING ||
response.data.status === ApiCaptiq.STATUS_IN_PROGRESS ||
response.data.status === ApiCaptiq.STATUS_PRE_WAITING
) {
history.push(`/view/${loanId}`);
} else {
setLoanApplication(response.data);
}
},
[loanId, authContext, history],
);
const loadLoanApplication = useCallback(
async (loan_id: number) => {
try {
const response = await request.get(`${ApiCaptiq.LOAN_APPLICATION_URL}${loan_id}/`);
const { fetchCustomerProfile } = customerContext;
await fetchCustomerProfile(response.data.customer_profile_id);
onLoadLoanApplication(response);
} catch (err) {
onLoadFail();
}
},
[customerContext, onLoadLoanApplication, onLoadFail],
);
...
What could be the possible reason for this
The functions you are defining inside the component, are not just available on the component instance. In fact, there is not way to call them. You can test only by mocking the fetch calls they are doing.
If you really need callable functions in your component (you should try to avoid these..), you could use this: https://reactjs.org/docs/hooks-reference.html#useimperativehandle
Perhaps better would be to extract this data loading logic elsewhere and test it separately.

Can't set state in react

So, I'm simply trying to set state in my react app. Simply get data from Axios, and then set state. But no matter what I do, the state will not set. I've tried putting it in a callback since it's async and putting it my component did mount and component did update alas nothing. any pointers?
class App extends Component {
componentDidUpdate() {}
constructor(props) {
super(props);
this.state = {
Catogories: [
"Business",
"Entertainment",
"General",
"Health",
"Science",
"Sports",
"Technology"
],
CatPics: [],
TopStories: [],
Selection: [],
Sources: [],
selected: false
};
}
GeneratePic = () => {
this.state.Catogories.forEach(Catogory => {
axios
.get(
"https://api.pexels.com/v1/search?query=" +
Catogory +
"&per_page=15&page=1",
{
Authorization:
"563492ad6f91700001000001d33b5d31a9a145b78ee67e35c8e6c321"
}
)
.then(res => {
var object = { Catogory: res.photos[0].src.large2x };
this.state.CatPics.push(object);
});
});
};
dump = x => {
this.setState({ TopStories: x }, console.log(this.state.TopStories));
};
TopStories = () => {
console.log("working");
axios
.get(
"https://newsapi.org/v2/top-headlines?country=us&apiKey=91bec895cf8d45eaa46124fb19f6ad81"
)
.then(res => {
console.log(res);
const data = res.data.articles;
console.log(data);
this.dump(data);
});
};
You are doing two things wrong.
Don't mutate the state
Don't do async actions inside loop and then use same loop variable inside async callback because at that point in time, loop variable will have some other value and not the respective iteration category.
GeneratePic = async () => {
const promises = this.state.Catogories.map(Catogory => {
return axios
.get(
"https://api.pexels.com/v1/search?query=" +
Catogory +
"&per_page=15&page=1",
{
Authorization:
"563492ad6f91700001000001d33b5d31a9a145b78ee67e35c8e6c321"
}
)
.then(res => {
return res.photos[0].src.large2x;
});
});
let photos = await Promise.all(promises);
photos = this.state.Catogories.map((cat, index) => ({ [cat]: photos[index] }));
this.setState({ CatPics: photos });
};
getPics = cat => {
return axios
.get(
"https://api.pexels.com/v1/search?query=" +
cat +
"&per_page=15&page=1",
{
Authorization:
"563492ad6f91700001000001d33b5d31a9a145b78ee67e35c8e6c321"
}
)
.then(res => {
return { [cat]: res.photos[0].src.large2x };
});
}
GeneratePic = async () => {
const promises = this.state.Catogories.map(Catogory => {
this.getPics(Catogory);
});
let photos = await Promise.all(promises);
this.setState({ CatPics: photos });
};
This should work.
Dont Mutate the state.
GeneratePic = () => {
this.state.Catogories.forEach(async Catogory => {
await axios
.get(
"https://api.pexels.com/v1/search?query=" +
Catogory +
"&per_page=15&page=1", {
Authorization: "563492ad6f91700001000001d33b5d31a9a145b78ee67e35c8e6c321"
}
)
.then(res => {
var object = { Catogory: res.data.photos[0].src.large2x };
const cPics = [...this.state.CatPics];
cPics.push(object);
this.setState({
CatPics: cPics
})
});
});
};

Categories