Why is my set data with useState not updating the state? - reactjs

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;

Related

when re rendering react, object is null

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

Why doesn't the axios response get saved in useState variable

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;

Fetch runs in a loop

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>
);
}

map array of objects from an api react

I have been trying to get the images from this API to append to the page by mapping through them, but I keep getting one of two error messages saying 'undefined.map is not a function' or 'getBirds.map is not a function.'I've tried leaving the array of objects as it is and setting state to an object and an array(at separate times) but that didn't work. I've also tried using Object.key, Object.values, and Object.entries(each at separate times) to turn the array of objects into an array and then map through my variable and through getBirds(again separately) but those attempts also failed. I have attached three of my attempts below. Can someone help me understand where I've gone wrong?
// Attempt 1
import {useState, useEffect} from 'react'
import axios from 'axios'
function Birds(props) {
const [getBirds, setGetBirds] = useState({})
const {image} = props
useEffect(() => {
async function fetchBirds() {
const URL = `https://audubon-society-api.herokuapp.com/birds`
try {
const res = await axios.get(URL)
console.log(res.data)
setGetBirds(res.data)
} catch (error) {
console.log(error)
}
}
fetchBirds()
}, [])
if (!getBirds) return <h3>Loading...</h3>
return (
<div>
<img src={getBirds.map(image)} alt={getBirds.map(image)}></img>
</div>
)
}
export default Birds
// Attempt 2
import {useState, useEffect} from 'react'
import axios from 'axios'
function Birds(props) {
const [getBirds, setGetBirds] = useState([])
const {image} = props
useEffect(() => {
async function fetchBirds() {
const URL = `https://audubon-society-api.herokuapp.com/birds`
try {
const res = await axios.get(URL)
console.log(res.data)
setGetBirds(res.data)
} catch (error) {
console.log(error)
}
}
fetchBirds()
}, [])
if (!getBirds) return <h3>Loading...</h3>
return (
<div>
<img src={getBirds.map(image)} alt={getBirds.map(image)}></img>
</div>
)
}
export default Birds
// Attempt 3
import {useState, useEffect} from 'react'
import axios from 'axios'
function Birds(props) {
const [getBirds, setGetBirds] = useState({})
const {image} = props
useEffect(() => {
async function fetchBirds() {
const URL = `https://audubon-society-api.herokuapp.com/birds`
try {
const res = await axios.get(URL)
console.log(res.data)
setGetBirds(res.data)
} catch (error) {
console.log(error)
}
}
fetchBirds()
}, [])
const birds = Object.entries(getBirds)
birds.forEach(([key, value]) => {
console.log(key, value)
})
if (!getBirds) return <h3>Loading...</h3>
return (
<div>
<img src={birds.map(image)} alt={birds.map(image)}></img>
</div>
)
}
export default Birds
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
You would need to initialize your state with an array, so the map function won't get errors, and correct the way you map it:
Initialize state with an array:
const [getBirds, setGetBirds] = useState([]);
Map it:
return (
<div>
{getBirds.map((bird) => (
<img src={bird.image} alt={bird.image}></img>
))}
</div>
);
Also, check your array with length, because [] or {} both equal to true.
if (!getBirds.length) return <h3>Loading...</h3>;
console.log(!![]);
console.log(!!{});
console.log(!![].length)
The completed solution:
import { useState, useEffect } from "react";
import axios from "axios";
function Birds(props) {
const [getBirds, setGetBirds] = useState([]);
useEffect(() => {
async function fetchBirds() {
const URL = 'https://audubon-society-api.herokuapp.com/birds';
try {
const res = await axios.get(URL);
console.log(res.data);
setGetBirds(res.data);
} catch (error) {
console.log(error);
}
}
fetchBirds();
}, []);
if (!getBirds.length) return <h3>Loading...</h3>;
return (
<div>
{getBirds.map((bird) => (
<img src={bird.image} alt={bird.image}></img>
))}
</div>
);
}
export default Birds;
Working Example:
Your init state of birds and setBirds should be an array [] not an object {}, also you don't need:
const birds = Object.entries(getBirds). fetch return array of birds already.
<img src={birds.map(image)} alt={birds.map(image)}></img> is wrong, the array loop map should render an image for each bird.
Below code will run for your need:
import React, {useState, useEffect} from "react";
import axios from 'axios';
function Birds(props) {
//- const [getBirds, setGetBirds] = useState([])
//- const {image} = props
// +
const [birds, setGetBirds] = useState([])
useEffect(() => {
async function fetchBirds() {
const URL = `https://audubon-society-api.herokuapp.com/birds`
try {
const res = await axios.get(URL)
console.log(res.data)
setGetBirds(res.data)
} catch (error) {
console.log(error)
}
}
fetchBirds()
}, [])
// - const birds = Object.entries(getBirds)
// - birds.forEach(([key, value]) => {
// - console.log(key, value)
// - })
// - if (!getBirds) return <h3>Loading...</h3>
if (!birds) return <h3>Loading...</h3>
return (
<div>
{/* <img src={birds.map(image)} alt={birds.map(image)}></img> */}
{birds.map((item, index) =>
<img src={item.image} alt={index}></img>
)}
</div>
)
}
export default Birds

Get single data from state in React

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]);

Resources