Working with a simple card component, to which I load data from an API with Axios get by using UseEffect. The purpose is that when the component is loaded, it loads the data to it once.
The issue is that data is not loaded correctly on load but repeatedly after it when the component updates. How can I make it so that it loads the data once when the page is loaded but not after that?
I have omitted the API url but it is tested and returns data correctly from backend.
Card component:
import React from 'react';
import { Card, CardImg, CardBody, CardTitle, CardSubtitle, Container, Row, Col } from 'reactstrap';
import { useEffect, useState } from "react";
import { APIGet } from '../API.js';
function RenderCardLoadMore() {
const [data, setData] = useState([]);
const resource = 'search';
const params = { 'limit': '2' };
const results = APIGet(resource, params);
useEffect(() => {
console.log("initialload");
setData(results);
}, []);
return (
<Container fluid>
<Container>
<Row>
{data.map((v, i) => (
<Col className="py-2" key={i} xs={12} md={4} lg={3}>
<Card className="explore_event_card shadow">
<CardBody>
<CardTitle className="card_header">{v.Title}</CardTitle>
<CardSubtitle>{v.SubTitle}</CardSubtitle>
</CardBody>
</Card>
</Col>
))}
</Row>
</Container>
</Container>
);
}
export default RenderCardLoadMore;
API component:
import { useEffect, useState } from 'react';
import axios from 'axios';
const API_URL = 'https://myapi/'; /*url omitted for this question, tested and working*/
const session_id = localStorage.getItem("session_id");
export function APIGet (resource, params) {
const [data, setData] = useState([]);
const url = API_URL + resource;
params['session_id'] = session_id;
useEffect(() => {
axios.get(url, {params: params}).then((v) => {
setData(v.data)
}).catch( (err) => console.log(["APIGet error:", err]) )
}, [url]); //url as 2nd argument not needed?
return data;
}
You could remove url and use an empty dependency array so the useEffect hook is triggered only once after the initial render. From what I can tell though, APIGet doesn't need to be a hook and doesn't need to use the useState hook. I can simply return the Promise chain returned from axios.
API
import axios from 'axios';
const API_URL = 'https://myapi/.....';
export function APIGet (resource = "", params = {}) {
const session_id = JSON.parse(localStorage.getItem("session_id"));
const url = API_URL + resource;
params['session_id'] = session_id;
return axios.get(url, {params: params})
.then((v) => {
return v.data
})
.catch((err) => console.log(["APIGet error:", err]));
}
RenderCardLoadMore - Call APIGet in the useEffect hook and update the state when Promise resolves.
import { APIGet } from '../API.js';
function RenderCardLoadMore() {
const [data, setData] = useState([]);
const resource = 'search';
const params = { 'limit': '2' };
useEffect(() => {
console.log("initialload");
APIGet(resource, params)
.then(results => {
setData(results);
});
}, []);
return (....);
}
Related
im calling an object from the pokeapi, exactly the name property and on first render after saving the file i get the name but i dont know why, re render and then the propertie is null and i get an error
this is my component card
import {
EditOutlined,
EllipsisOutlined,
SettingOutlined,
} from "#ant-design/icons";
import { Avatar, Card, Col, Row } from "antd";
function Pokecard(values: any) {
const { response} = values;
const { Meta } = Card;
return (
<Row gutter={[10, 10]}>
<Col>
<Card
style={{ width: 300 }}
cover={
<img
alt={"" }
src={response && response['sprites']['front_default']}
/>
}
actions={[
<SettingOutlined key="setting" />,
<EditOutlined key="edit" />,
<EllipsisOutlined key="ellipsis" />,
]}
>
<Meta
avatar={<Avatar src="https://joeschmoe.io/api/v1/random" />}
title={response.name}
description=""
/>
</Card>
</Col>
</Row>
);
}
export default Pokecard;
this is my view
import { Methods } from "../interfaces/request";
import { useEffect, useState } from "react";
import Pokecard from "../components/pokecard/Pokecard";
import useAxios from "../plugins/Useaxios";
function App2() {
const { response, loading, error } = useAxios({
method: Methods["get"],
url: "/ditto",
body: JSON.stringify({}),
headers: JSON.stringify({}),
});
const [data, setData] = useState([]);
useEffect(() => {
if (response !== null) {
setData(response);
}
}, [response]);
let args: any = {
response,
};
return (
<>
<Pokecard {...args} />;
</>
);
}
export default App2;
and this is my plugin axios
import axios from "axios";
import Request from "../interfaces/request";
import { useState, useEffect } from "react";
enum Methods {
get = "get",
post = "post",
default = "get",
}
const useAxios = ({ url, method, body, headers }: Request) => {
axios.defaults.baseURL = "https://pokeapi.co/api/v2/pokemon";
const [response, setResponse] = useState(null);
const [error, setError] = useState("");
const [loading, setloading] = useState(true);
const fetchData = () => {
axios[method](url, JSON.parse(headers), JSON.parse(body))
.then((res: any) => {
setResponse(res.data);
})
.catch((err: any) => {
setError(err);
})
.finally(() => {
setloading(false);
});
};
useEffect(() => {
fetchData();
}, [method, url, body, headers]);
return { response, error, loading };
};
export default useAxios;
im learning to destructuring objects
im tried saving the object in the store but i got an Undifined
sorry for my english
you can try something like this
title={response?.name || ''}
Try using the resonse directly
const { response, loading, error } = useAxios({
method: Methods["get"],
url: "/ditto",
body: JSON.stringify({}),
headers: JSON.stringify({}),
});
const name = response?.name;
const src = response?.sprites?.?front_default;
// use the properties directly inside the child
return (
<>
<Pokecard name={name} src={src}/>
</>
);
You can check examples of how when useEffect is not needed
I've built a random photo displaying feature in react.
the console says that the response is valid and it works,
but the page breaks when I return data.
Where is the issue?
Thanks in advance!
import React from 'react'
import { useEffect, useState } from 'react'
import axios from 'axios'
function RandomPhoto() {
const url = `https://api.unsplash.com/photos/random/?client_id=${process.env.REACT_APP_UNSPLASH_KEY}`
const [data, setData] = useState()
const getPhoto = () => {
axios.get(url)
.then(response => {
setData(response.data)
console.log(response.data) // <------- works
})
.catch(error => {
console.log(error)
})
}
useEffect(() => {
getPhoto()
},[])
console.log("XX" + data) // <---------- doesn't work, and following return() neither
return (
<div>
<img href={data.urls.regular} alt={data.alt_description}/>
<p>Photo by {data.username} {data.name} from {data.location} - found on unsplash</p>
</div>
)
}
export default RandomPhoto
I modified your code a bit, and it's working. I made it as an async function and changed the path of JSON object keys.
Please note the location data sometimes returns as null. So you have to render it conditionally.
import React from 'react';
import { useEffect, useState } from 'react';
import axios from 'axios';
const RandomPhoto = () => {
const url = `https://api.unsplash.com/photos/random/?client_id=${process.env.REACT_APP_UNSPLASH_KEY}`;
const [imageData, setImageData] = useState('');
const getPhoto = async () => {
await axios
.get(url)
.then((response) => {
setImageData(response.data);
})
.catch((error) => {
console.log(error);
});
};
useEffect(() => {
getPhoto();
}, []);
return (
<div>
<p>Hello</p>
<img src={imageData.urls?.regular} />
<p>
Photo by {imageData?.user?.username} {imageData?.user?.name} from{' '}
{imageData?.location?.country} - found on unsplash
</p>
</div>
);
};
export default RandomPhoto;
I'm trying to make a page to show the details of each video.
I fetched multiple video data from the back-end and stored them as global state.
This code works if I go to the page through the link inside the app. But If I reload or open the URL directory from the browser, It can not load the single video data.
How should I do to make this work?
Thanx
Single Video Page
import { useState, useEffect, useContext } from "react";
import { useParams } from "react-router-dom";
import { VideoContext } from "../context/videoContext";
const SingleVideo = () => {
let { slug } = useParams();
const [videos, setVideos] = useContext(VideoContext);
const [video, setVideo] = useState([]);
useEffect(() => {
const result = videos.find((videos) => {
return videos.uuid === slug;
});
setVideo((video) => result);
}, []);
return (
<>
<div>
<h1>{video.title}</h1>
<p>{video.content}</p>
<img src={video.thumbnail} alt="" />
</div>
</>
);
};
export default SingleVideo;
Context
import React, { useState, createContext, useEffect } from "react";
import Axios from "axios";
import { AxiosResponse } from "axios";
export const VideoContext = createContext();
export const VideoProvider = (props) => {
const [videos, setVideos] = useState([]);
const config = {
headers: { "Access-Control-Allow-Origin": "*" },
};
useEffect(() => {
//Fetch Vidoes
Axios.get(`http://localhost:5000/videos`, config)
.then((res: AxiosResponse) => {
setVideos(res.data);
})
.catch((err) => {
console.log(err);
});
}, []);
return (
<VideoContext.Provider value={[videos, setVideos]}>
{props.children}
</VideoContext.Provider>
);
};
I think the reason is because when you refresh the app, you fetch the video data on context and the useEffect on your single video page component runs before you receive those data.
To fix you can simply modify slightly your useEffect in your single video component to update whenever you receive those data:
useEffect(() => {
if (videos.length) {
const result = videos.find((videos) => {
return videos.uuid === slug;
});
setVideo((video) => result);
}
}, [videos]);
When I call API from single useEffect, it works perfectly. But when I am trying to call another API from another useEffect in the same component its shows a error.
If it's possible, please have a look at my project on codesandbox.
import React, { useEffect, useState } from 'react';
import { Container, Row, Col } from 'react-bootstrap';
const TeacherDashboard = () => {
// console.log(props)
const [appointmentList, setAppointmentList] = useState([]);
const [viewProfile, setViewProfile] = useState([]);
console.log(viewProfile);
useEffect(() => {
async function loadData(){
const response = await fetch('http://localhost:4200/appointments')
const data = await response.json();
setAppointmentList(data)
}
loadData()
}, [appointmentList])
useEffect(() => {
async function proData() {
const response = await fetch('http://localhost:4200/schedule')
const data = await response.json();
setViewProfile(data)
}
proData()
}, [viewProfile])
return (
<Container>
<Row>
<Col>
{
appointmentList.map(app =>
<div style={{border: '1px solid blue'}}>
<li>Name : {app.name} </li>
<li>Id : {app.s_id} </li>
<li>Sec : {app.sec} </li>
<li>Email : {app.email} </li>
<li>Date & Time : {app.dateTime} </li>
</div>
)
}
</Col>
</Row>
</Container>
);
};
export default TeacherDashboard;
I am not sure the purpose of setting both appointmentList and viewProfile states as the part of the dependency arrays of both useEffect hooks. Both of them will eventually result in an infinite loop as you are directly updating the respective states in the useEffect hooks.
From what I can see, you only need to make both requests once, thus you should be using an empty array as the dependency array, such that both requests will be called only when the component is mounted. This is how it can be done:
useEffect(() => {
async function proData() {
const response = await fetch('http://localhost:4200/schedule')
const data = await response.json();
setViewProfile(data)
}
proData();
async function loadData(){
const response = await fetch('http://localhost:4200/appointments')
const data = await response.json();
setAppointmentList(data)
}
loadData();
}, []);
The useEffect below renders, fetches data, and displays it once (using an empty array for 2nd parameter in useEffect).
I need it to rerun useEffect everytime the user changes data to the database (when user uses axios.post).
What i've tried
using [tickets], but that just causes the useEffect to run infinitly
also using [tickets.length] and [tickets, setTickets]
trying to use props as parameter but didnt find anything useful
import React, { useState, createContext, useEffect } from "react";
import axios from "axios";
export const TicketContext = createContext();
export const TicketProvider = (props) => {
console.log(props);
const [tickets, setTickets] = useState([]);
useEffect(() => {
getTickets();
console.log("1", { tickets });
}, []);
const getTickets = async () => {
const response = await axios.get("http://localhost:4000/tickets/");
setTickets(response.data);
};
return <TicketContext.Provider value={[tickets, setTickets]}>{props.children}
</TicketContext.Provider>;
};
import React from "react";
import { useState, useEffect, useContext } from "react";
import Ticket from "../Ticket";
import { TicketContext } from "../contexts/TicketContext";
import AddBacklog from "../addData/AddBacklog";
const TicketDisplay = (props) => {
const [tickets, setTickets] = useContext(TicketContext);
return (
<div className="display">
<p>Antony Blyakher</p>
<p>Number of Tickets: {tickets.length}</p>
<div className="backlog">
<h1>Backlog</h1>
{tickets.map((currentTicket, i) => (
<div className="ticketBlock">
<Ticket ticket={currentTicket} key={i} />
</div>
))}
</div>
</div>
);
const AddBacklog = (props) => {
const [tickets, setTickets] = useState("");
...
axios.post("http://localhost:4000/tickets/add", newTicket).then((res) => console.log(res.data));
setTickets((currentTickets) => [...currentTickets, { name: name, status: "backlog", id: uuid() }]);
};
You'll need to watch for tickets and return if it has data to not cause infinite loop:
useEffect(() => {
if (tickets.length) return // so, we call just once
getTickets();
console.log("1", { tickets });
}, [tickets]);
const fetchData = () => {
axios.get("http://localhost:7000/api/getData/").then((response) => {
console.log(response.data);
if (response.data.success) {
SetIsLoading(false);
}
setDataSource(response.data.data);
});
};
useEffect(() => {
fetchData();
if (fetchData.length) fetchData();
}, [fetchData]);
by this you can fetch the data in real-time as any change in data occurs.