I am fetching data in my react component
import React, { useState, useEffect } from 'react';
import { fetchBookData } from './bookData';
import "./App.css";
export default function Books ({ books }) {
const [payload, setPayload] = useState(null)
fetchBookData(books).then((payload) => setPayload(payload));
return (
<div className="App">
<h1>Hello</h1>
</div>
);
}
Here is the fetch function itself
const dev = process.env.NODE_ENV !== 'production';
const server = dev ? 'http://localhost:3001' : 'https://your_deployment.server.com';
// later definable for developement, test, production
export const fetchBookData = (books) => {
const options = {
method: `GET`,
headers: {
accept: 'application/json',
},
};
return fetch(`${server}/books`, options)
.then((response) => {
if(response.ok){
return response.json()
}
throw new Error('Api is not available')
})
.catch(error => {
console.error('Error fetching data in book data: ', error)
})
}
But when I start the server fetch runs in a loop, component making endless get requests to the server. I tried to wrap it in a useEffect, but didn't work. Fetch should run once on load
If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array ([]) as a second argument. More
example (codesandbox)
export default function App({books}) {
const [payload, setPayload] = useState(null);
useEffect(() => {
fetchBookData(books).then((payload) => setPayload(payload));
}, [books]);
if (!payload) {
return <h1>Loading...</h1>;
}
return (
<div className="App">
<h1>{payload.title}</h1>
</div>
);
}
Related
Hello I am developing a todo list app using reactjs with axios. I managed to view, and add data to the database, my problem now is that I dont know how to load the updated data after submitting the form.
This is the code for fetching all the data from the database. The file name is FetchData.js
import { useEffect, useState} from 'react';
import axios from 'axios';
const FetchData = () => {
const [data, setData] = useState({});
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
try {
const { data: response } = await axios.get('http://localhost/todolistci/backend/index.php/todos/view', { crossDomain: true });
setData(response);
} catch (error) {
console.error(error);
}
setLoading(false);
};
fetchData();
}, []);
return {
data,
loading,
};
};
export default FetchData;
This is how I view the list of items came from FetchData.js. The file name is List.js
import React from 'react';
import ListItem from './ListItem'
import FetchData from './FetchData';
function List() {
const {
data,
loading,
} = FetchData();
return (
<ul>
{loading && <div>Loading</div>}
{!loading && (
<>
{data.map(item => (<ListItem key={item.id} id={item.id} name={item.name} complete={item.complete} />))}
</>
)}
</ul>
)
}
export default List
Now this is the form That I am submitting. File name is FormToDo.js
import React, {useState} from 'react';
import axios from 'axios';
function FormToDo() {
const [formValue, setformValue] = useState({
name: '',
});
const handleSubmit = async(e) => {
e.preventDefault();
// store the states in the form data
const nameFormData = new FormData();
nameFormData.append("name", formValue.name)
try {
// make axios post request
const response = await axios({
method: "post",
url: "http://localhost/todolistci/backend/index.php/create",
data: nameFormData,
headers: { "Content-Type": "multipart/form-data" },
});
} catch(error) {
console.log(error)
}
//empty the text field
setformValue({name: ''});
//I need to update the list of data in here
}
const handleChange = (event) => {
setformValue({
...formValue,
[event.target.name]: event.target.value
});
}
return (
<div>
<form onSubmit={handleSubmit}>
<input type="text" name="name" id="name" required placeholder="Enter To Do"
value={formValue.name} onChange={handleChange} onKeyDown={handleChange} />
<button type="submit">+</button>
</form>
</div>
)
}
export default FormToDo
This is the image of the todo app I am making.
enter image description here
Please help me. Thank you.
Your example doesn't describe how you are going back to the list after axios posted the data and got a response.
What you need is to mutate after database is updated.
one way could be to move "fetchData" from useEffect to "FetchData" and add a a mutate function that fetches the data and is made available in the return
const FetchData = () => {
const [data, setData] = useState({});
const [loading, setLoading] = useState(true);
const fetchData = async () => {
try {
const { data: response } = await axios.get(
"http://localhost/todolistci/backend/index.php/todos/view",
{ crossDomain: true }
);
setData(response);
} catch (error) {
console.error(error);
}
setLoading(false);
};
const mutate = () => fetchData();
useEffect(() => {
fetchData();
}, []);
return {
data,
loading,
mutate,
};
};
and then call mutate after data is posted.
A second solution could be to push the browser to the list page and make sure fetchData runs.
A third solution (and the solution I would choose) is to use for example SWR - React Hooks for Data Fetching that would help you to fetch & mutate data, you can see axios example in their docs
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;
setAcessKeys is not updating the state immediately even though the data is available at that point which I know through the console.log(data) or by passing the array manually. I realized that without useEffect, it renders multiple times and the console.log(accessKeys) returns data from the third run going.
import { useState, useEffect } from "react";
import axios from "axios";
import AccessKey from "./AccessKey";
import { toast } from "react-toastify";
import { useNavigate } from "react-router-dom";
import "./AccessKey.module.css";
const AccessKeys = () => {
const [accessKeys, setAccessKeys] = useState([]);
const navigate = useNavigate();
useEffect(() => {
const getAccessKeys = async () => {
try {
let token = localStorage.getItem("auth");
const response = await axios.get(
"http://localhost:5000/api/keys/user",
{
headers: {
authorization: `Bearer ${token}`,
},
}
);
const data = response.data;
console.log(data); // [{...}, {...}]
setAccessKeys((prevKeys) => [...prevKeys, ...data]);
console.log(accessKeys); // []
} catch (error) {
navigate("/");
toast.error(error.response.data.message);
}
};
getAccessKeys();
}, [navigate, accessKeys]);
return (
<>
{accessKeys.length > 0 ? (
<main>
{accessKeys.map((accessKey) => (
<AccessKey key={accessKey._id} acesskey={accessKey} />
))}
</main>
) : (
<h4>You do not have any Access Keys at the moment</h4>
)}
</>
);
};
export default AccessKeys;
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]);
I'm trying to fetch some data from an api and display it in jsx.First I get the users geolocation,then I call the fetch function which uses the users geolocation data to request the data from an api , afterwards
the received data from an api is used to set the weatherData state.The final step is where conditional rendering is used to show the h1 element depending if the state is defined or not.The problem is that my weatherData is always undefined,and when I try to display it returns as undefined error.Why is my weatherData undefined?
import react from "react";
import {useState} from "react";
import {useEffect} from "react";
const MainWeather=()=>{
{/*State for storing geolocation data*/}
const [status, setStatus] = useState(null);
const [weatherData,setWeatherData]=useState('');
{/*Fetches the data from an api*/}
const fetchData=(link)=>{
fetch(link)
.then(res => res.json())
.then(
(result)=>{
{/*Sets the weather data object*/}
setWeatherData(result);
console.log(result);
setStatus('data set');
},
(error)=>{
console.log(error)
}
)
}
{/*Retrieves the location from geolocation api*/}
const getLocation = async () => {
if (!navigator.geolocation) {
setStatus('Geolocation is not supported by your browser');
} else {
navigator.geolocation.getCurrentPosition((position) => {
{/*Calls the fetch function to get the data from an api*/}
fetchData(`https://api.openweathermap.org/data/2.5/onecall?lat=${position.coords.latitude}&lon=${position.coords.longitude}&exclude={part}&appid=0ea4f961aae42bfa56f75ca058577e1e&units=metric`);
}, () => {
setStatus('Unable to retrieve your location');
});
}
}
{/*Calls getLocation function on the first render*/}
useEffect(()=>{getLocation()},[])
console.log(status);
return(
<div>
{weatherData == 'undefined' ?
<h1>undefined</h1> :
<h1>{weatherData.current.temp}</h1> }
</div>
)
}
export default MainWeather;
I checked the code, what you have implemented is correct , if you are using a mac you should allow browser to fetch location , in windows a popup will come to allow it , might be browser issue check it again
still I made few changes in below the above code , just refer to it
import { useState, useEffect } from "react";
const MainWeather = () => {
const [status, setStatus] = useState(null);
const [weatherData, setWeatherData] = useState("");
const fetchData = (link) => {
fetch(link)
.then((res) => res.json())
.then(
(result) => {
setWeatherData(result);
setStatus("data set");
},
(error) => {
console.log(error);
}
);
};
const getLocation = async () => {
if (!navigator.geolocation) {
setStatus("Geolocation is not supported by your browser");
} else {
navigator.geolocation.getCurrentPosition(
(position) => {
fetchData(
`https://api.openweathermap.org/data/2.5/onecall?lat=${position.coords.latitude}&lon=${position.coords.longitude}&exclude={part}&appid=0ea4f961aae42bfa56f75ca058577e1e&units=metric`
);
},
() => {
setStatus("Unable to retrieve your location");
}
);
}
};
useEffect(() => {
getLocation();
}, []);
return (
<div>
{!weatherData ? (
<h1>{status}</h1>
) : (
<h1>{weatherData?.current?.temp} ℃ </h1>
)}
</div>
);
};
export default MainWeather;
You can refer to this codesandbox