I have a problem making a request to the StarWars API - reactjs

I am a person who is learning to use APIs with react and i have a problem because my code does not work. I get the following error in the console: Uncaught TypeError: Cannot read properties of undefined (reading 'map') App.jsx:24
.However I have optional chaining applied.
I show you my code and I hope you can help me
useFetch.js
import axios from "axios"
import { useEffect, useState } from "react"
const useFetch = url => {
const [response, setResponse] = useState()
useEffect( () => {
axios.get(url)
.then(res=>setResponse(res.data))
.catch(err=> console.log(err.message))
}, [])
return response
}
export default useFetch
App.jsx
import axios from 'axios'
import { useEffect, useState } from 'react'
import './App.css'
import FormInput from './components/FormInput'
import Residents from './components/Residents'
function App() {
const [planets, setPlanets] = useState()
const [response, setResponse] = useState()
const number = parseInt(response)
const updatePage = number => {
const URL = `https://swapi.dev/api/planets/${number}/`
axios.get(URL)
.then(res => setPlanets(res.data))
.catch(err => console.log(err.message))
}
useEffect(() => {
const URL = `https://swapi.dev/api/planets/${number}/`
axios.get(URL)
.then(res => updatePage())
.catch(err => console.log(err.message))
}, [])
console.log(number)
return (
<div className="App">
<FormInput
setResponse={setResponse}
/>
<hr />
{
planets?.residents.map(resident => (
<Residents
resident={resident}
key={resident.name}
/>
))
}
</div>
)
}
export default App
Residents.jsx
import React from 'react'
import useFetch from '../hooks/useFetch'
const Residents = ({ resident }) => {
const users = useFetch(resident)
console.log(users)
return (
<article>
</article>
)
}
export default Residents

It's because your residents is not an array. Make it array first or something you are storing in resident is not a object and map() method only works on array not on object. So fix it.
planets?.residents?.map()
use "?" after residents and your residents is not an array. So you need to make it array or whatever you are storing in you residents variable is not an array please cross check you variable/state.

Related

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;

Axios with random API

I am trying to use random API in my application using axios and I get error that "TypeError: randomData.map is not a function". Maybe someone could tell me what I'm doing wrong?
import './App.css';
import {useEffect, useState} from "react";
import axios from 'axios';
function App() {
const [randomData, setRandomDate] = useState([])
useEffect(() => {
axios.get('https://randomuser.me/api')
.then(res => {
console.log('Getting from: ', res.data)
setRandomDate(res.data)
})
.catch(err => console.log(err))
}, [])
const arr = randomData.map((data, index) => {
return (
<p>{data}</p>
)
})
return (
<div className="App">
<h1>Learning React:</h1>
{arr}
</div>
);
}
export default App;
Your issue is that randomuser api returns an object
{
info: ...,
results: [{}]
}
so when you setRandomData to just res.data, you are assigning an object to your state, and map is not part of an object prototype.
What you need to do is to replace setRandomDate(res.data) with setRandomDate(res.data.results) and now you will be able to map that array.
like
const arr = randomData.map((data, index) => {
return (
<p>{data.email}</p>
)
})

Fetch data from api in createContext using Fetch api in React doesn't work

I have an existing context for products. Where initially I used some mock data as shown below STORE_DATA to render the components. Now I need to replace that mock data and connect to a Node.js api which is available on my local port (created the api I after I created the react-app).
import React, { createContext, useState } from 'react';
import STORE_DATA from '../shop';
export const ProductsContext = createContext();
const ProductsContextProvider = ({ children }) => {
const [products] = useState(STORE_DATA);
return (
<ProductsContext.Provider value={{ products }}>
{
children
}
</ProductsContext.Provider>
);
}
export default ProductsContextProvider;
Just created a helper.js file witht he following to fetch the data:
import {useEffect} from "react";
const fetchData = () => {
return fetch("https://localhost:8081/products") <<tested on postman and works fine.
.then((response) => response.json())
.then((data) => console.log('Fetching Data:',data));
}
How to replace the mock data on the context file and use this fetchData() using useEffect within the context? What code should change?
Tried the following, but didn't work, can't even print the console.log:
import React, { createContext, useState, useEffect } from 'react';
import { fetchData } from '../helpers';
export const ProductsContext = createContext();
const ProductsContextProvider = ({ children }) => {
const [products, setProducts] = useState(null);
useEffect(() => {
setProducts(fetchData());
}, []);
return (
<ProductsContext.Provider value={{ products }}>
{
children
}
</ProductsContext.Provider>
);
}
export default ProductsContextProvider;
The issue was that it was returning the following error (explained):
net::ERR_SSL_PROTOCOL_ERROR (on chrome)
Solution: Use http:// instead of https:// in the URL's in the following code:
const fetchData = () => {
return fetch("http://localhost:8081/products")
.then((response) => response.json())
.then((data) => console.log('Fetching Data:',data));
}

Failed to Compile error - Unexpected token

This is a React app - Here is my code:
NewsContext.js
import React, { createContext, useEffect, useState } from "react";
import axios from 'axios'
export const NewsContext = createContext()
export const NewsContextProvider = (props) => {
const [data, setData] = useState([])
const apikey = "e25c25eb946e48779021963c288ce5cc";
useEffect(() => {
axios
.get(
'https://newsapi.org/v2/everything?q=tesla&from=2021-06-21&sortBy=publishedAt&apiKey=${apiKey}'
)
.then(response => setData(response.data))
.catch(error => console.log(error));
}, [data]);
return (
<NewsContextProvider props={data}>
{props.children}
</NewsContextProvider>,;
);
};
But when I run it I get this error:
Copy of error
./src/NewsContext.js
SyntaxError: C:\Users\HP\Documents\GitHub\newsfinder-app\src\NewsContext.js: Unexpected token (22:31)
20 |
21 | {props.children}
22 | ,;
| ^
23 | );
24 | };
This code works for me:
import React, {createContext, useEffect, useState} from "react"
import axios from "axios"
export const NewsContext = createContext()
export const NewsContextProvider = (props) => {
const [data, setData] = useState([])
const apikey = "e25c25eb946e48779021963c288ce5cc"
const apiUrl = `https://newsapi.org/v2/everything?q=tesla&from=2021-06-21&sortBy=publishedAt&apiKey=${apikey}`
useEffect(() => {
axios
.get(
apiUrl
)
.then(response => setData(response.data))
.catch(error => console.log(error))
}, [data, apiUrl])
return (
<NewsContextProvider props={data}>
{props.children}
</NewsContextProvider>
)
}
and in the index.js just do:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import {NewsContextProvider} from "./NewContextProvider"
ReactDOM.render(
<React.StrictMode>
<NewsContextProvider>
<App />
</NewsContextProvider>
</React.StrictMode>,
document.getElementById('root')
);
From what I understood from your code is, there are two errors
Remove the ,; from return block
And use backticks instead of single quotation. It's called Template Literals
And the Last one is Passing the data in array in useEffect, it'll cause an infinite loop when the component will render, just pass an empty array, it means it will run only once when the component mounts
axios
.get(
`https://newsapi.org/v2/everything?q=tesla&from=2021-06-
21&sortBy=publishedAt&apiKey=${apiKey}`)
// should be backticks
.then(response => setData(response.data))
.catch(error => console.log(error));
And In useEffect
const apiRequest = () =>{
const apikey = "e25c25eb946e48779021963c288ce5cc";
axios
.get(
`https://newsapi.org/v2/everything?q=tesla&from=2021-06-21&sortBy=publishedAt&apiKey=${apiKey}`
)
.then(response => setData(response.data))
.catch(error => console.log(error));
}
useEffect(() => {
apiRequest();
}, []); // you should not pass data,
// as it will cause an infinitie loop in the useEffect, just pass empty array to avoid the loop
So the Code will look like
import React, { createContext, useEffect, useState } from "react";
import axios from 'axios'
export const NewsContext = createContext()
export const NewsContextProvider = (props) => {
const [data, setData] = useState([])
//const apikey = "e25c25eb946e48779021963c288ce5cc";
const apiRequest = () =>{
const apikey = "e25c25eb946e48779021963c288ce5cc";
axios
.get(
`https://newsapi.org/v2/everything?q=tesla&from=2021-06-21&sortBy=publishedAt&apiKey=${apiKey}`
)
.then(response => setData(response.data))
.catch(error => console.log(error));
}
useEffect(() => {
apiRequest();
}, []);
return (
<NewsContextProvider props={data}>
{props.children}
</NewsContextProvider>
);
};

this axios api call returns an empty array for the call, but the second one returns a value

I am trying to use the context below to fetch the data from the api, then pass it down to other components. The data is from unsplash, so I would like to filter it out first using a keyword set by the user. However, the first array returned by the function is empty, but if I console.log() it, I get all the data as expected
import React, {useState, createContext} from 'react';
import axios from 'axios';
export const ImageContext = createContext();
export const ImageContextProvider = ({children}) => {
const [images, setImages] = useState([]);
const count = 15;
const start = 1;
const getImages = async (keyword) => {
await axios.get(`api/photos?count=${count}&start=${start}&keyword=${keyword}`)
.then(res => setImages(res.data.results));
}
return (
<ImageContext.Provider value={{images, getImages: getImages }}>
{children}
</ImageContext.Provider>
);
}
This is the component that passes the keyword to the context that will be used to fetch data from the API
import React, { useState, useContext} from 'react';
import { ImageContext } from '../../Context/ImageContext';
export const SearchBar = () => {
const { getImages, images } = useContext(ImageContext);
const[keyword, setKeyword] = useState('');
const searchImages = async (e) => {
e.preventDefault();
await getImages(keyword);
console.log(images);
}
return (
<form onSubmit={searchImages}>
<input type='text' placeholder='Search Images' value={keyword} onChange={(e) =>
setKeyword(e.target.value)}/>
<input type='submit'/>
</form>
);
}
You are almost there :)
Since you are using React hooks, you need useEffect so that you can be able to send Http request. It is equivalent to `componentDidMount.
How to use it?
import React, {useState, createContext, useEffect} from 'react';
useEffect(() => {
const result = axios.get(`api/photos?count=${count}&start=${start}&keyword=${keyword}`)
.then(res => setImages(res.data.results)); // remember to update your state
}, []); // don't forget to pass an empty array
Take a look at your getImages() function:
const getImages = async (keyword) => {
await axios.get(`api/photos?...`).then(...);
}
If I’m right that is a wrong construction as you’re mixing and await and a then(). Furthermore, your getImages() is not returning a value. I think the following solves your problem:
const getImages = async (keyword) => {
const res = await axios.get(`api/photos?...`);
setImages(res.data.results);
return res.data.results;
}

Resources