getting "Cannot read property "map" of undefined - reactjs

I am getting "Cannot read property "map" of undefined (it is referring to tradeData) from the TradeDataList.js file. I am calling an API that is called through search term that is passed to "unirest" get function. I have setup context API passing some centralized values to various other files. I have it set up as an array in the useState. I am loosing my head with this if anyone can help please. The files I have are the following:
context.js
import React, { useState, useContext, useEffect } from 'react'
import { useCallback } from 'react'
var unirest = require('unirest');
const url = 'https://api.someurl.com/oauth/client_credential/accesstoken' ;
const Browse_URL = 'https://api.someurl.com/tradesregister/v1/browse' ;
const MY_API_KEY = 'EpVhCPGX4lzMwzdXVKG7yYFubGtwmlYU4343434';
const Authorization = 'Basic RXBWaENQR1g0bHpNd3pkWFZLRzd5WUZ1Ykd0d21sWVU6M2NvcEVuQTVEZlJXZ3BSYw==' ;
const AppContext = React.createContext()
const AppProvider = ({ children }) => {
const [token, setToken] = useState('') ;
const [loading, setLoading] = useState(true) ;
const [tradeData, setTradeData] = useState([]) ;
const [searchTerm, setSearchTerm] = useState('')
const getToken = () => {
unirest.get(url)
.header({'Accept': 'application/json', 'authorization': Authorization})
.query({"grant_type": "client_credentials"})
.end(function (response) {
const token = response.body["access_token"] ;
//console.log(token) ;
setToken(token)
})
}
useEffect (() => {
// call the getToken function
getToken() ;
}, [])
//Get the new token to access trade data
const newAuth = 'Bearer ' + token
return (
<AppContext.Provider
value={{ token,
MY_API_KEY,
newAuth,
Browse_URL,
loading,
setTradeData,
tradeData,
setLoading,
searchTerm,
setSearchTerm
}}
>
{children}
</AppContext.Provider>
)
}
export const useGlobalContext = () => {
return useContext(AppContext)
}
export { AppContext, AppProvider }
SearchForm.js (call starts from here )
import React, { useEffect, useState } from 'react'
import { useGlobalContext } from '../context'
import TradeDataList from './TradeDataList'
//import Loading from './Loading'
var unirest = require('unirest');
const SearchForm = () => {
const {searchTerm, setSearchTerm,loading, setLoading, tradeData, setTradeData, MY_API_KEY, newAuth, Browse_URL } = useGlobalContext();
//const [tradeData, setTradeData] = useState([]) ;
const getTrade = () => {
setLoading(true)
unirest.get(Browse_URL)
.header({'Accept': 'application/json', 'authorization': newAuth, "apikey": MY_API_KEY })
.query({"searchText": searchTerm })
.end(function (response) {
const tradeData = response.body ;
//console.log(tradeData) ;
setTradeData(tradeData)
setLoading(false);
})
}
useEffect (() => {
// call the getTrade function
getTrade() ;
}, [])
const handleSubmit = (e) => {
e.preventDefault() ;
getTrade() ;
}
console.log('here is the data' ,tradeData)
return (
<section className="section-search">
<form className="search-form" onSubmit={handleSubmit}>
<div className="form-control">
<label htmlFor='searchTerm'> search trade licence info</label>
<input
type='text'
id='searchTerm'
//ref={searchValue} //reference
value={searchTerm}
//onChange={searchTrade}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</div>
<button type="submit">Submit</button>
</form>
<div className="cocktails-center">
<TradeDataList tradeData={tradeData} />
</div>
</section>
)
}
export default SearchForm
TradeList.js
import React from 'react'
import TradeData from './TradeData'
//import Loading from './Loading'
//import { useGlobalContext } from '../context'
/**
* This page displays TradeData and loading
*/
const TradeDataList = ({ tradeData }) => {
//display loading while cocktails are being loaded
return (
<section className="section">
<h2 className="section-title">
Trade Data
</h2>
{/* <h2>cocktail list</h2> */}
<div className="cocktails-center">
{tradeData.map((trade) => {
// this is to be handled by Cocktail component
return <TradeData key={trade.licenceID} {...trade} />
})}
</div>
</section>
)
}
export default TradeDataList
TradeData.js
import React from 'react'
import { Link } from 'react-router-dom'
//image,name,id,info,glass
const TradeData = ({
licenceID,
licensee,
licenceName,
licenceNumber,
licenceType,
status,
suburb,
postcode,
businessNames,
categories,
classes
}) => {
return (
<article className="cocktail">
<div className="img-container">
<h1>This is header </h1>
</div>
<div className="cocktail-footer">
<h3>{licenceID}</h3>
<p>{licensee}</p>
<h4>{licenceName}</h4>
<p>{licenceType}</p>
{/* <Link to={`/cocktail/${id}`} className="btn btn-primary
btn-details">details</Link> */}
</div>
</article>
)
}
export default TradeData

Inside your TradeList.js you simply need to add a check condition:
<div className="cocktails-center">
{tradeData?tradeData.map((trade) => {
// this is to be handled by Cocktail component
return <TradeData key={trade.licenceID} {...trade} />
}):null}
</div>

Another alternative solution
{ tradeData && tradeData.map((trade) => {
// this is to be handled by Cocktail component
return <TradeData key={trade.licenceID} {...trade} />
})}
Note: The .map function is only available on array.
If data isn't in the format you are expecting it to be (it is {} but you are expecting []).

Related

How to get data when language changed in react V18.2?

I have problem with fetching data when language changed. I tried a lot of things that I found from Stack overflow, but unfortunately it just changing the direction and it didn't fetch the data based on language changed.
I fetching data with a custom hook and call it inside my functional component. let me share the code that I write.
Note: I'm using I18nextLng for translation.
App.js
import { RouterProvider } from "react-router-dom";
import Loading from './components/loading';
import routes from './routes/routes';
import { useEffect } from "react";
import i18n from "./utils/i18n";
function App() {
useEffect(() => {
let cleanup = true;
if (cleanup) {
i18n.on('languageChanged', (local) => {
let direction = i18n.dir(local);
document.body.dir = direction;
})
}
return () => {
cleanup = false;
};
}, []);
return (
<RouterProvider router={routes} fallbackElement={<Loading />} />
)
}
export default App;
LanguageSwitcher.js
import { useTranslation } from "react-i18next";
const LanguageSwitcher = () => {
const { i18n } = useTranslation();
return (
<select
className="form-select-sm rounded-pill text-center"
aria-label="Default select example"
value={i18n.language}
onChange={(e) =>
i18n.changeLanguage( e.target.value)
}
>
<option value="en">English</option>
<option value="fa">دری</option>
</select>
);
}
export default LanguageSwitcher;
Internships.js
import Image from "react-bootstrap/Image"
import { useFetchWebsiteData } from "../../hooks/website/useFetchWebsiteData";
import Loading from '../../components/loading'
import { useEffect, useState } from "react";
const Internships = () => {
let lang = localStorage.getItem("i18nextLng")
const { data, isLoading } = useFetchWebsiteData("getInternship", lang);
console.log("language changed", language);
return !isLoading ? (
<div className="container-fluid news-wrapper">
<div className="container">
<div className="row py-5">
<div className="col-md-12">
<div className="col-md-8">
<h4 className="title mb-4">{data?.title}</h4>
<p className="sub-title">{data?.content}</p>
</div>
<div className="col-md-2 text-center">
<Image
src={require("../../images/internships.png")}
fluid={true}
/>
</div>
</div>
</div>
</div>
</div>
) : (
<Loading />
);
}
export default Internships;
useFetchWebsiteData.js (Custom hook for fetching data)
import { useState, useEffect } from "react";
import { axiosPublic } from "../../utils/axios";
export const useFetchWebsiteData = (url,lang) => {
const [data, setData] = useState({});
const [isLoading, setIsLoading] = useState(true);
const [isError, setIsError] = useState(false);
// const lang = localStorage.getItem("i18nextLng");
console.log('lang inside hook', lang)
useEffect(() => {
// const controller = new AbortController()
const fetchData = async () => {
setIsLoading(true);
await axiosPublic
.get(url, {
headers: { lang: lang === "fa" ? "dr" : "en" },
// signal: controller.signal,
})
.then((response) => {
if (response.status === 200) {
if (lang === "en") {
setIsLoading(false);
response.data.data.en.map((d) => {
let title = d.title;
let content = d.content;
return setData({ title: title, content: content });
});
}
if (lang === "fa") {
setIsLoading(false);
console.log("fa intern", response.data.data.dr)
response.data.data.dr.map((d) => {
let title = d.title;
let content = d.content;
return setData({ title: title, content: content });
});
setIsLoading(false);
}
} else {
setIsError(true);
}
})
.catch((error) => {
setIsLoading(false);
setIsError(true);
console.error(error.message);
});
};
fetchData();
// return () => {
// controller.abort()
// };
}, [url, lang]);
return { data, isLoading, isError };
};
I really appreciate for your helping.

TypeError: weatherData.map is not a function

I'm trying to map over data from API, but while writing the code to display the data I got this error: TypeError: weatherData.map is not a function
I tried removing useEffect from the code and tried to add curly brackets: const [weatherData, setWeatherData] = useState([{}])
Update: Line 14 log undefined : console.log(weatherData.response)
import axios from 'axios'
import { useEffect, useState } from 'react'
import './App.css'
function App() {
const [search, setSearch] = useState("london")
const [weatherData, setWeatherData] = useState([])
const getWeatherData = async () => {
try {
const weatherData = await axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${search}&appid={APIKEY}`);
console.log(weatherData.response);
if (weatherData) {
setWeatherData(weatherData);
}
} catch (err) {
console.error(err);
}
}
useEffect(() => {
getWeatherData()
}, [getWeatherData])
const handleChange = (e) => {
setSearch(e.target.value)
}
return (
<div className="App">
<div className='inputContainer'>
<input className='searchInput' type="text" onChange={handleChange} />
</div>
{weatherData.map((weather) => {
return (
<div>
<h1>{weather.name}, {weather.country}</h1>
</div>
)
})}
</div>
)
}
export default App
You're having errors in fetching the data as well as rendering it.
Just change the entire App component like this :
import { useEffect, useState } from "react";
import axios from "axios";
function App() {
const [search, setSearch] = useState("London");
const [weatherData, setWeatherData] = useState([]);
const APIKEY = "pass your api key here";
useEffect(() => {
const fetchData = async () => {
const result = await axios(
`https://api.openweathermap.org/data/2.5/weather?q=${search}&appid=${APIKEY}`
);
setWeatherData(result.data);
};
fetchData();
}, [search]);
const handleChange = (e) => {
setSearch(e.target.value);
};
return (
<div className="App">
<div className="inputContainer">
<input className="searchInput" type="text" onChange={handleChange} />
</div>
<h1>
{" "}
{weatherData.name} ,{" "}
{weatherData.sys ? <span>{weatherData.sys.country}</span> : ""}{" "}
</h1>
</div>
);
}
export default App;
this should be working fine just make sure to change : const APIKEY = "pass your api key "; to const APIKEY = "<your API key> ";
this is a demo in codesandbox
Create a promise function:
const getWeatherData = async () => {
try {
const weatherData = await axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${search}&appid={APIKEY}`);
console.log(weatherData.response);
if (weatherData.response.data) {
setWeatherData(weatherData.response.data);
}
} catch (err) {
console.error(err);
}
}
Then call it.

ReactJS Error when using map function, cannot read properties of undefined

I'm trying to make a sport/tinder like app for a school project from a friend of mine. It came together well on my localhost, but for him it was a requirement to host it online. Not really a professional in hosting, but I was a bit familiar with Heroku. I used a client and a server side for my application, so I build the client side and put it into the server side folder. This server side is hosted on the Heroku page. But whenever I try to login, it won't work and I get this error message in my console.
TypeError: Cannot read properties of undefined (reading 'map')
The error says it is caused by this line of code.
const matchedUserIds = matches.map(({user_id}) => user_id)
This is the whole MatchDisplay file that is used in my Dashboard. I'm using a MongoDB for the storage of my users.
import axios from "axios";
import { useEffect, useState } from "react";
import { useCookies } from "react-cookie";
const MatchesDisplay = ({ matches, setClickedUser }) => {
const [matchedProfiles, setMatchedProfiles] = useState(null);
const [cookies, setCookie, removeCookie] = useCookies(null);
const [matched, setMatched] = useState(null);
const matchedUserIds = matches.map(({ user_id }) => user_id);
const userId = cookies.UserId;
const getMatches = async () => {
try {
const response = await axios.get(
"https://[app].herokuapp.com/users",
{
params: { userIds: JSON.stringify(matched()) },
}
);
setMatchedProfiles(response.data);
} catch (error) {
console.log(error);
}
};
useEffect(() => {
getMatches();
}, [matches]);
const filteredMatchedProfiles = matchedProfiles?.filter(
(matchedProfile) =>
matchedProfile.matches.filter(
(profile) => profile.user_id === userId
).length > 0
);
return (
<div className="matches-display">
{filteredMatchedProfiles?.map((match) => (
<div
key={match.user_id}
className="match-card"
onClick={() => setClickedUser(match)}
>
<div className="img-container">
<img
src={match?.url}
alt={match?.first_name + "profile"}
/>
</div>
<h3>{match?.first_name}</h3>
</div>
))}
</div>
);
};
export default MatchesDisplay;
Any help is welcome. If you need more code examples, please reply ;)
EDIT
The ChatContainer that passes the user to the MatchesDisplay.
import ChatHeader from "./ChatHeader";
import MatchesDisplay from "./MatchesDisplay";
import ChatDisplay from "./ChatDisplay";
import { useState } from 'react';
const ChatContainer = ({user}) => {
const [ clickedUser, setClickedUser] = useState(null)
return (
<div className="chat-container">
<ChatHeader user={user}/>
<div>
<button className="option" onClick={() => setClickedUser(null)}>Matches</button>
<button className="option" disabled={!clickedUser}>Chat</button>
<button className="option" >Prices</button>
</div>
{!clickedUser && <MatchesDisplay matches={user.matches} setClickedUser={setClickedUser}/>}
{clickedUser && <ChatDisplay user={user} clickedUser={clickedUser}/>}
</div>
)
}
export default ChatContainer
The Dashboard that passes the user to the Chatcontainer.
import TinderCard from 'react-tinder-card';
import {useEffect, useState} from 'react';
import {useCookies} from 'react-cookie';
import ChatContainer from '../components/ChatContainer'
import axios from "axios";
const Dashboard = () => {
const [user, setUser] = useState(null)
const [genderedUsers, setGenderedUsers] = useState(null)
const [lastDirection, setLastDirection] = useState(null)
const [cookies, setCookie, removeCookie] = useCookies(['user'])
const [matchedUserIds, setMatchedUserIds] = useState(null)
const [filteredGenderedUsers, setFilteredGenderedUsers] = useState(null)
const userId = cookies.UserId
const getUser = async () => {
try {
const response = await axios.get('https://funfit-webpage.herokuapp.com/user', {
params: {userId}
})
return setUser(response.data)
} catch (error) {
console.log(error)
}
}
const getGenderedUsers = async () => {
try {
const response = await axios.get('https://funfit-webpage.herokuapp.com/gendered-users', {
params: {gender: user?.gender_interest}
})
return setGenderedUsers(response.data)
} catch (error) {
console.log(error)
}
}
useEffect(() => {
getUser()
}, [])
useEffect(() => {
setMatchedUserIds(user?.matches.map(({user_id}) => user_id).concat(userId))
if (user) return getGenderedUsers()
}, [user])
useEffect(() => {
if (genderedUsers) {
return setFilteredGenderedUsers(genderedUsers?.filter(
genderedUser => !matchedUserIds.includes(genderedUser.user_id)
))
}
}, [genderedUsers])
const updateMatches = async (matchedUserId) => {
try {
await axios.put('https://funfit-webpage.herokuapp.com/addmatch', {
userId,
matchedUserId
})
return getUser()
} catch (error) {
console.log(error)
}
}
const swiped = (direction, swipedUserId) => {
console.log(direction, swipedUserId)
if (direction === 'right') {
updateMatches(swipedUserId)
}
return setLastDirection(direction)
}
const outOfFrame = (name) => {
console.log(name + ' left the screen!')
}
return (<>
{user && <div className="dashboard">
<ChatContainer user={user}/>
<div className="swipe-container">
<div className="card-container">
{filteredGenderedUsers?.map((genderedUser) =>
<TinderCard
className='swipe'
key={genderedUser.user_id}
onSwipe={(dir) => swiped(dir, genderedUser.user_id)}
onCardLeftScreen={() => outOfFrame(genderedUser.first_name)}>
<div style={{backgroundImage: 'url(' + genderedUser.url + ')'}} className='card'>
<h3>{'Name: ' + genderedUser.first_name} <br/> {'Sport: ' + genderedUser.about}</h3>
</div>
</TinderCard>)}
<div className="swipe-info">
{lastDirection ? <p>You swiped {lastDirection}</p> : <p/>}
</div>
</div>
</div>
</div>}
</>)
}
export default Dashboard

useEffect keep getting executed every time even dependency not changed

We have UserContext which sets user object which we can use throughout application. Our UserContext keep executing every time and unnecessary making api call even though dependency hasn't changed.
import React, { useState, useEffect } from 'react'
import APIService from './utils/APIService';
import { getCookies } from './utils/Helper';
const UserContext = React.createContext();
const UserContextProvider = (props) => {
const [token, setToken] = useState(getCookies('UserToken'));
const [user, setUser] = useState(null);
useEffect(() => {
console.log('Inside userContext calling as token ', token)
fetchUserInfo();
}, [token]);
const fetchUserInfo = async() => {
if (token) {
let userRes = await APIService.get(`/user?token=${token}`);
console.log('User route called')
setUser(userRes.data);
}
}
/*
If user logoff or login, update token from child component
*/
const refreshToken = (newToken) => {
//token = newToken;
setToken(newToken);
fetchUserInfo()
}
return (
<UserContext.Provider value={{user, token, refreshToken}}>
{props.children}
</UserContext.Provider>
);
}
export { UserContextProvider, UserContext }
Whenever we navigate to different page in our react app, we are seeing "User" route being called every time even though token isn't updated. Our token changes only when user log off.
Our AppRouter looks like following;
import React from 'react';
import AppRouter from "./AppRouter";
import { Container } from 'react-bootstrap';
import Header from './components/Header';
import { ToastProvider, DefaultToastContainer } from 'react-toast-notifications';
import 'bootstrap/dist/css/bootstrap.css';
import './scss/styles.scss';
import { UserContextProvider } from './UserContextProvider';
export default function App() {
const ToastContainer = (props) => (
<DefaultToastContainer
className="toast-container"
style={{ zIndex:100,top:50 }}
{...props}
/>
);
return (
<UserContextProvider>
<ToastProvider autoDismiss={true} autoDismissTimeout={3000} components={{ ToastContainer }}>
<Container fluid>
<Header />
<AppRouter />
</Container>
</ToastProvider>
</UserContextProvider>
)
}
This is our internal app so we want user to be logged in for 30 days and they don't have to keep login every time. So when user login first time, we create a token for them and keep that token in cookies. So if user close the browser and come back again, we check token in cookies. If token exists, we make API call to fetch user information and setUser in our context. This is the part which isn't working and it keep calling our user api during navigation to each route in application.
Here is our login.js
import React, { useState, useContext } from 'react';
import { setCookies } from '../../utils/Helper';
import APIService from '../../utils/RestApiService';
import { UserContext } from '../../UserContextProvider';
import queryString from 'query-string';
import './_login.scss';
const Login = (props) => {
const [email, setEmail] = useState(null);
const [password, setPassword] = useState(null);
const [error, setError] = useState(null);
const { siteId } = props;
const { refreshToken} = useContext(UserContext);
const onKeyPress = (e) => {
if (e.which === 13) {
attemptLogin()
}
}
let params = queryString.parse(props.location.search)
let redirectTo = "/"
if (params && params.redirect)
redirectTo = params.redirect
const attemptLogin = async () => {
const payload = {
email: email,
password: password,
siteid: siteId
};
let response = await APIService.post('/login', payload);
console.log('response - ', response)
if (response.status === 200) {
const { data } = response;
setCookies('UserToken', data.token);
refreshToken(data.token)
window.location.replace(redirectTo);
}
else {
const { error } = response.data;
setError(error);
}
}
const renderErrors = () => {
return (
<div className="text-center login-error">
{error}
</div>
)
}
return (
<div className="login-parent">
<div className="container">
<div className="login-row row justify-content-center align-items-center">
<div className="login-column">
<div className="login-box">
<form className="login-form form">
<h3 className="login-form-header text-center">Login</h3>
<div className="form-group">
<label>Email:</label>
<br/>
<input
onChange={e => setEmail(e.target.value)}
placeholder="enter email address"
type="text"
onKeyPress={onKeyPress}
className="form-control"/>
</div>
<div className="form-group">
<label>Password:</label>
<br/>
<input
onChange={e => setPassword(e.target.value)}
placeholder="enter password"
type="password"
className="form-control"/>
</div>
<div className="form-group">
<button
className="btn btn-secondary btn-block"
onClick={attemptLogin}
type="button">
Login
</button>
</div>
{error ? renderErrors() : null}
</form>
</div>
</div>
</div>
</div>
</div>
)
}
export default Login;
Our userContext looks like below
import React, { useState, useEffect } from 'react'
import APIService from './utils/APIService';
import { getCookies } from './utils/Helper';
const UserContext = React.createContext();
const UserContextProvider = (props) => {
const [token, setToken] = useState(getCookies('UserToken'));
const [user, setUser] = useState(null);
useEffect(() => {
if (!token) return;
console.log('Inside userContext calling as token ', token)
fetchUserInfo();
}, [token]);
const fetchUserInfo = async() => {
if (token) {
let userRes = await APIService.get(`/user?token=${token}`);
console.log('User route called')
setUser(userRes.data);
}
}
/*
If user logoff or login, update token from child component
*/
const refreshToken = (newToken) => {
//token = newToken;
setToken(newToken);
fetchUserInfo()
}
return (
<UserContext.Provider value={{user, token, refreshToken}}>
{props.children}
</UserContext.Provider>
);
}
export { UserContextProvider, UserContext }
Our getCookies function which simply read cookies using universal-cookies package
export const getCookies = (name) => {
return cookies.get(name);
};
So I tried to replicate your issue using a CodeSandbox, and these are my findings based on your code:
Context:
Your context has a useEffect which depend on token. When you call refreshToken, you update the token which automatically triggers the useEffect and makes a call to fetchUserInfo. So you don't need to call fetchUserInfo after setToken in refreshToken. Your context would look like:
const UserContext = React.createContext();
const UserContextProvider = (props) => {
const [token, setToken] = useState(getCookies("UserToken"));
const [user, setUser] = useState(null);
useEffect(() => {
console.log("Inside userContext calling as token ", token);
fetchUserInfo();
}, [token]);
const fetchUserInfo = async () => {
if (token) {
let userRes = await APIService.get(`/user?token=${token}`);
console.log('User route called')
setUser(userRes.data);
}
};
const refreshToken = (newToken) => {
setToken(newToken);
};
return (
<UserContext.Provider value={{ user, token, refreshToken }}>
{props.children}
</UserContext.Provider>
);
};
export { UserContextProvider, UserContext };
Route:
Now coming to your routing, since you've not included code of AppRouter I had to make an assumption that you use react-router with Switch component. (As shown in CodeSandbox).
I see a line in your Login component which is window.location.replace(redirectTo);. When you do this, the entire page gets refreshed (reloaded?) and React triggers a re-render, which is why I suppose your context methods fire again.
Instead use the history API from react-router (Again, my assumption) like so,
let history = useHistory();
history.push(redirectTo);
Here's the sandbox if you want to play around:

React custom hook state not 'always there'

I thought had a better grasp of hooks but I've clearly got something wrong here. Not all of the character objects will have what I'm trying to get but it wont work with those that do.
I cna't even build in a check for character.comics.available. Same errors appear. I'm presuming I'm getting them before the state is set? But {character.name} always works.
CharacterDetail.js
import React from "react";
import { useParams } from "react-router-dom";
import useCharacter from "../hooks/useCharacter";
const CharacterDetail = () => {
// from the Route path="/character/:id"
const { id } = useParams();
// custom hook. useCharacter.js
const [character] = useCharacter(id);
// this only works sometimes but errors if i refresh the page
// console.log(character.comics.available);
return (
<div>
<h2 className="ui header">Character Details</h2>
<p>Works every time: {character.name}</p>
<div className="ui segment"></div>
<pre></pre>
</div>
);
};
export default CharacterDetail;
Custom hook useCharacter.js
import { useState, useEffect } from "react";
import marvel from "../apis/marvel";
const useCharacter = (id) => {
const [character, setCharacter] = useState({});
useEffect(() => {
loadItem();
return () => {};
}, [id]);
const loadItem = async (term) => {
const response = await marvel.get(`/characters/${id}`);
console.log(response.data.data.results[0]);
setCharacter(response.data.data.results[0]);
};
return [character];
};
export default useCharacter;
error when console is uncommented
Uncaught TypeError: Cannot read property 'available' of undefined
at CharacterDetail (CharacterDetail.js:11)
...
Here is the character object.
thanks to #Nikita for the pointers. Settled on this...
CharacterDetail.js
import React from "react";
import { useParams } from "react-router-dom";
import useCharacter from "../hooks/useCharacter";
const CharacterDetail = () => {
const { id } = useParams();
// custom hook. useCharacter.js
const { isLoading, character } = useCharacter(id);
const isArray = character instanceof Array;
if (!isLoading && isArray === false) {
console.log("isLoading", isArray);
const thumb =
character.thumbnail.path +
"/portrait_uncanny." +
character.thumbnail.extension;
return (
<div>
<h2 className="ui header">{character.name}</h2>
<img src={thumb} />
<div className="ui segment">{character.comics.available}</div>
<div className="ui segment">{character.series.available}</div>
<div className="ui segment">{character.stories.available}</div>
</div>
);
}
return <div>Loading...</div>;
};
export default CharacterDetail;
useCharacter.js
import { useState, useEffect } from "react";
import marvel from "../apis/marvel";
function useCharacter(id) {
const [character, setCharacter] = useState([]);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
setIsLoading(true);
const fetchData = async () => {
setIsLoading(true);
await marvel
.get(`/characters/${id}`)
.then((response) => {
/* DO STUFF WHEN THE CALLS SUCCEEDS */
setIsLoading(false);
setCharacter(response.data.data.results[0]);
})
.catch((e) => {
/* HANDLE THE ERROR (e) */
});
};
fetchData();
}, [id]);
return {
isLoading,
character,
};
}
export default useCharacter;

Resources