React won't render multiple fetched API's to the DOM - javascript

Link to CodeSandbox.
I can successfully display Tavares' fetched data to the DOM through a .map statement. However, once I try to load up the second and third players data using the exact same way (they're there, just commented out right now) - under Tavares' .map - I get thrown an error of, "Cannot read property 'map' of undefined", and the first .map (Matthews) after Tavares'.
Trying to figure out why this error is displaying, and why I can't map all three data points to the DOM.
App.JS
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import axios from "axios";
import "./styles.css";
function App() {
// Set initial state for data
const [data, setData] = useState({ tavares: [], matthews: [], marner: [] });
// Fetch data
useEffect(() => {
const fetchData = async () => {
// Grab all players API's
let tavares =
"https://statsapi.web.nhl.com/api/v1/people/8475166?expand=person.stats&stats=yearByYear,careerRegularSeason&expand=stats.team&site=en_nhlCA";
let matthews =
"https://statsapi.web.nhl.com/api/v1/people/8479318?expand=person.stats&stats=yearByYear,careerRegularSeason&expand=stats.team&site=en_nhlCA";
let marner =
"https://statsapi.web.nhl.com/api/v1/people/8478483?expand=person.stats&stats=yearByYear,careerRegularSeason&expand=stats.team&site=en_nhlCA";
// Axios to get all api's
axios
.all([axios.get(tavares), axios.get(matthews), axios.get(marner)])
.then(
axios.spread((tavares, matthews, marner) => {
setData(
{ tavares: [tavares.data.people[0]] },
{ matthews: [matthews.data.people[0]] },
{ marner: [marner.data.people[0]] }
);
console.log("Tavares:", tavares.data.people[0]);
console.log("Matthews:", matthews.data.people[0]);
console.log("Marner:", marner.data.people[0]);
})
);
};
fetchData();
}, []);
return (
<>
<h1>Tavares</h1>
<ul>
{data.tavares.map(item => (
<li key={item.objectID}>
<p>{item.id}</p>
<p>{item.primaryNumber}</p>
</li>
))}
</ul>
{/* <h1>Matthews</h1>
<ul>
{data.matthews.map(item => (
<li key={item.objectID}>
<p>{item.id}</p>
<p>{item.primaryNumber}</p>
</li>
))}
</ul>
<h1>Marner</h1>
<ul>
{data.marner.map(item => (
<li key={item.objectID}>
<p>{item.id}</p>
<p>{item.primaryNumber}</p>
</li>
))}
</ul> */}
</>
);
}
export default App;
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

You are calling setData with three arguments (three objects with one player each) rather than an object with all players. This means only the first object with only tavares is getting set as your data (try logging your data object before rendering to see).
You are doing:
setData(
{ tavares: [tavares.data.people[0]] },
{ matthews: [matthews.data.people[0]] },
{ marner: [marner.data.people[0]] }
);
When you should be doing:
setData({
tavares: [tavares.data.people[0],
matthews: [matthews.data.people[0]],
marner: [marner.data.people[0]]
});

Related

{Answered} React Shows Data in console but does not render it on page

I am super new to React and have just started my journey with it I have another page where I use the same function of handleIndexEmployees and the same const variables with useState and useEffect which load just fine. Right now with the code I have the only thing that shows up is
<h1>Employees</h1>
import { useState, useEffect } from "react";
import axios from "axios";
import { EmployeesAxios } from "./EmployeesAxios";
export function EmployeesIndex() {
const [employees, setEmployees] = useState([]);
const handleIndexEmployees = () => {
console.log("Wait I'm getting Da Minions");
axios.get("http://localhost:3000/employees.json").then((response) => {
console.log("Hello my Minions");
console.log(response.data);
setEmployees(response.data);
});
};
useEffect(handleIndexEmployees, []);
return (
<div>
<EmployeesAxios employee={employees} />
</div>
);
}
export function EmployeesAxios(props) {
return (
<div>
<h1>Employees</h1>
{props.employee.map((employee) => {
<div key={employee.id}>
<h3>Name: {` ${employee.first_name} ${employee.last_name}`}</h3>
</div>;
})}
</div>
);
}
{props.todos.map((todo) => (
<div key={todo.id}>
<h2>{todo.name}</h2>
<p>{todo.description}</p>
<button
onClick={() => {
axios.patch(`http://localhost:3000/todos/${todo.id}.json`).then((response) => {
console.log(response);
});
}}
>
{todo.done}
Done
</button>
<button onClick={() => props.onShowTodo(todo)}>More info</button>
</div>
))}
I tried putting this all on one page with making all the requests on that page, and I get the same response with that so my page that I am rendering for a selector wheel to assign tasks to that employee works just fine with using props so that is why I tried moving all the axios requests to another page and passing in props. I also tried using isLoading as well and this didn't work either.
It looks like you are not returning anything from your map.
Add a return statement here
{
props.employee.map((employee) => {
// Return something from the map
return (
<div key={employee.id}>
<h3>Name: {` ${employee.first_name} ${employee.last_name}`}</h3>
</div>
);
});
}

How to render react element from a mapped array using object literal to get values

Hi do you render a react elements from an array of strings using object literal to get its value? let say for example I got an array of strings and I want to mapped on it using a function that returns an object literal with predefined values, if the strings matched return the react element. Here's what i've tried but doesn't display the icons
import { useCallback, useEffect, useMemo } from "react";
import PersonIcon from "#mui/icons-material/Person"
import ShoppingCartIcon from '#mui/icons-material/ShoppingCart'
import SecurityIcon from '#mui/icons-material/Security'
import LocalActivityIcon from '#mui/icons-material/LocalActivity'
export default function App() {
const iconNames = useMemo(()=>['PersonIcon','ShoppingCartIcon', 'SecurityIcon', 'LocalActivityIcon'],[]);
const getIcons = (icon) =>{
const icons = {
PersonIcon: <PersonIcon />,
ShoppingCartIcon: <ShoppingCartIcon />,
SecurityIcon: <SecurityIcon />,
LocalActivityIcon: <LocalActivityIcon />
}
return icons[icon];
}
const displayIcons = useCallback((arr) => {
return <ul>{arr.map((icon) => { return getIcons[icon] })}</ul>
},[])
useEffect(()=>{
displayIcons(iconNames)
})
return (
<div className="App">
<h1>Icons</h1>
{displayIcons}
</div>
);
}
Assuming that the icons are imported and static, perhaps the icons object could be defined outside of the component, so that even without useMemo or useCallback, it would not be re-created when the component re-renders.
The getIcons might not be necessary to access the icons in the object, as in the output JSX, the icons[name] can be wrapped in a curly braces {} to render the icon.
Example (live demo on: stackblitz):
import PersonIcon from '#mui/icons-material/Person';
import ShoppingCartIcon from '#mui/icons-material/ShoppingCart';
import SecurityIcon from '#mui/icons-material/Security';
import LocalActivityIcon from '#mui/icons-material/LocalActivity';
// 👇 Can be defined here if the icons are imported and static
const icons = {
PersonIcon: <PersonIcon />,
ShoppingCartIcon: <ShoppingCartIcon />,
SecurityIcon: <SecurityIcon />,
LocalActivityIcon: <LocalActivityIcon />,
};
// 👇 Just an array of the icon names for testing
const iconNames = Object.keys(icons);
export default function App() {
return (
<div className="App">
<h1>Icons</h1>
<ul>
{iconNames.map((name, index) => (
<li key={index}>{icons[name]}</li>
))}
</ul>
</div>
);
}
However depending on the use case, perhaps also consider to use other approaches instead of accessing it from an object to display a list of icons.
The example below displays a list of icons, and could also pass the MUI props to style it if needed (may not work for other libraries).
Example (live demo on: stackblitz):
// Works for MUI icons but might not for other libraries
const icons = [PersonIcon, ShoppingCartIcon, SecurityIcon, LocalActivityIcon];
export default function App() {
return (
<div className="App">
<h1>Icons</h1>
<ul>
{icons.map((Icon, index) => (
<li key={index}>
<Icon fontSize="large" color="primary" />
</li>
))}
</ul>
</div>
);
}
Just another possible approach that also works (with MUI) and accepts props for the icons, by defining the icon as component inside map:
Example (live demo on: stackblitz):
// Works for MUI icons but might not for other libraries
const icons = { PersonIcon, ShoppingCartIcon, SecurityIcon, LocalActivityIcon };
const iconNames = Object.keys(icons);
export default function App() {
return (
<div className="App">
<h1>Icons</h1>
<ul>
{iconNames.map((icon, index) => {
const Icon = icons[icon];
return (
<li key={index}>
<Icon fontSize="large" color="primary" />
</li>
);
})}
</ul>
</div>
);
}
You are calling displayIcons as a variable while this is a function.
getIcons is a function not an array. You should do getIcons(icon)
Your code can be reduce to this
import { useCallback, useEffect, useMemo } from "react";
import PersonIcon from "#mui/icons-material/Person"
import ShoppingCartIcon from '#mui/icons-material/ShoppingCart'
import SecurityIcon from '#mui/icons-material/Security'
import LocalActivityIcon from '#mui/icons-material/LocalActivity'
export default function App() {
const iconNames = ['PersonIcon','ShoppingCartIcon', 'SecurityIcon', 'LocalActivityIcon']
const getIcons = (icon) => {
const icons = {
PersonIcon: <PersonIcon />,
ShoppingCartIcon: <ShoppingCartIcon />,
SecurityIcon: <SecurityIcon />,
LocalActivityIcon: <LocalActivityIcon />
}
return icons[icon];
}
const displayIcons = useCallback(() => {
return (
<ul>{iconNames.map((icon) => { return getIcons(icon) })}</ul>
)
},[iconNames])
return (
<div className="App">
<h1>Icons</h1>
{displayIcons()}
</div>
);
}

How to Render this Array in array object

Hi I want to render this Advice on the screen but I could not do it I tried to map but that didn't helped please help me
import React, { useState, useEffect } from 'react'
export default function UsersData() {
const [Users, setUsers] = useState([{
"slip": {
"id": 41,
"advice": "Don't use Excel or Powerpoint documents for your basic word processing needs."
}
}])
return(
<>
<h2> React Fetch API Example </h2>
<ul>
{/* Not sure what to write here */}
</ul>
</>
)
}
<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>
I tried {Users.map(e=>{e.slip}) but it didn't work.
Using map function you can print whole array and sample code below here.
<ul>
{Users.map((element) => (
<li key={element.slip.id}>{element.slip.advice}</li>
))}
</ul>
It should be as simple as writing a mapping function:
export default function UsersData() {
const [Users, setUsers] = useState([
{
slip: {
id: 41,
advice:
"Don't use Excel or Powerpoint documents for your basic word processing needs.",
},
},
]);
return (
<>
<h2>React Fetch API Example</h2>
<ul>
{Users.map((user) => (
<li key={user.slip.id}>{user.slip.advice}</li>
))}
</ul>
</>
);
}
Here's a sample for your ref.
Use this:
import React, { useState, useEffect } from 'react'
export default function UsersData() {
const [Users, setUsers] = useState([
{
"slip": {
"id": 41,
"advice": "Don't use Excel or Powerpoint documents for your basic word processing needs."
}
}
])
return (
<>
<h2>React Fetch API Example</h2>
<ul>
{Users.map(({slip})=>(
<li key={slip.id}>{slip.advice}</li>
))}
</ul>
</>
)
}
<h2>React Fetch API Example</h2>
<ul>
{Users.map((user) =>
Object.keys(user).map((key) => (
<li>
{user[key].id} - {user[key].advice}
</li>
))
)}
</ul>

Next.js, getStaticProps not working with component but does with page

If I visit this code on local host, it is able to pull data from the API and then display it on a card.
import { formatNumber, parseTimestampJM } from '../../utils';
import { Card } from './UserTransactions.styled';
// STEP 1 : fetch data from api
export async function getStaticProps() {
const res = await fetch(
'https://proton.api.atomicassets.io/atomicmarket/v1/sales'
);
const data = await res.json();
return {
props: {
data,
},
};
}
function UserTransactionsComponent({ data }) {
const results = data;
console.log(results);
return (
<PageLayout>
<div>
<h1>This is a list of User Transactions!</h1>
</div>
<ul>
{results.data.map((result) => {
const {
sale_id,
buyer,
seller,
listing_price,
listing_symbol,
created_at_time,
} = result;
if (buyer !== null) {
return (
<Card>
<li key={sale_id}>
<h3>
{seller} just sold item number {sale_id} to {buyer} for{' '}
{formatNumber(listing_price)} {listing_symbol} at{' '}
{parseTimestampJM(created_at_time)}
</h3>
</li>
</Card>
);
}
})}
</ul>
</PageLayout>
);
}
export default UserTransactionsComponent;
When I create a component and then call it in to my index page like so:
<PageLayout>
<Banner modalType={MODAL_TYPES.CLAIM} />
<ExploreCard />
<HomepageStatistics />
<Title>New & Noteworthy</Title>
<UserTransactionsComponent />
<Grid items={featuredTemplates} />
</PageLayout>
);
};
export default MarketPlace;
it gives me the following error
TypeError: Cannot read properties of undefined (reading 'data')
27 | <ul>
> 28 | {results.data.map((result) => {
| ^
29 | const {
30 | sale_id,
31 | buyer,
I think that the reason I'm getting this error is because of the way the data is being fetched. Perhaps it's not being included in the component.
getStaticProps works only for page components inside pages folder. And the data is fetched at build time. If you wanna use UserTransactionsComponent as a normal component, you should use useEffect and make the API call on mount.
Here is what Next.js's documentation says about getStaticProps:
If you export a function called getStaticProps (Static Site Generation) from a page, Next.js will pre-render this page at build time using the props returned by getStaticProps.
Here is UserTransactionsComponent as a normal component:
import {useState, useEffect} from "react"
function UserTransactionsComponent() {
const [data, setData]=useState();
useEffect(()=>{
async function fetchData() {
const res = await fetch(
'https://proton.api.atomicassets.io/atomicmarket/v1/sales'
);
const {data} = await res.json();
setData(data)
}
fetchData()
},[]);
if(!data){
return (<div>Loading...</div>)
}
return (
<PageLayout>
<div>
<h1>This is a list of User Transactions!</h1>
</div>
<ul>
{data.map((result) => {
const {
sale_id,
buyer,
seller,
listing_price,
listing_symbol,
created_at_time,
} = result;
if (buyer !== null) {
return (
<Card>
<li key={sale_id}>
<h3>
{seller} just sold item number {sale_id} to {buyer} for{' '}
{formatNumber(listing_price)} {listing_symbol} at{' '}
{parseTimestampJM(created_at_time)}
</h3>
</li>
</Card>
);
}
})}
</ul>
</PageLayout>
);
}
export default UserTransactionsComponent;

React Hooks: Set All State Values to the Same Values with useState

New to React. I am trying to figure out how to set the state values of all objects with the values of a one specific object.
Here is the CodeSandbox: https://codesandbox.io/s/blissful-https-1o415
If you click on the Apply to All button in the Fork section, the data in Spoon and Knife would be updated to be the same as that in the Fork section. Is it possible to set the data of one object in the map, with that of another object? Would it happen in the map function or somewhere else? Thanks in advance for any help.
import React, { useState, useEffect } from "react";
import axios from "axios";
import "./styles.css";
function App() {
const [kitchenItems, setkitchenItems] = useState([]);
useEffect(() => {
axios
.get("./data.json")
.then((response) => setkitchenItems(response.data.kitchen));
}, []);
function applyAll(i) {
const updatedData = kitchenItems.map((item, idx) => {
if (idx === i) {
return {
...item,
size: item.size,
text: item.text,
value: item.value
};
}
return item;
});
setkitchenItems(updatedData);
console.log(kitchenItems);
}
return (
<main>
{kitchenItems.map((item, i) => (
<div className="utensil" key={i}>
<h2>{item.name}</h2>
<ul>
<li>{item.size}</li>
<li>{item.text}</li>
<li>{item.value}</li>
</ul>
<button type="button" name="apply" onClick={() => applyAll(i)}>
Apply to All
</button>
</div>
))}
</main>
);
}
export default App;
As stated in another answer, you will not see the update with the console log, since it would be an asynchronous and you would need to use a useEffect to see the update. You are close on your code though. Couple things:
You need to use a stable function for the applyAll (useCallback hook)
You were looking at index equal to the object you are trying to copy, and just using it's same values. This only changed spoon into an already spoon for it's values of size and more. What you wanted was to change the others. Here is an example I made on your codepen:
import React, { useCallback, useEffect, useState } from "react";
import axios from "axios";
import "./styles.css";
function App() {
const [kitchenItems, setkitchenItems] = useState([]);
useEffect(() => {
axios
.get("./data.json")
.then((response) => setkitchenItems(response.data.kitchen));
}, []);
const applyAll = useCallback((i) => {
const updatedData = kitchenItems.map((item, idx) => {
if (idx !== i) { //<-- You want to check if the index of the item doesn't equal the index of the item you want to change it to.
return {
...item,
size: kitchenItems[i].size, //<-- Since you have the index of the item, you can just call to get it's values here from kitchenItems
text: kitchenItems[i].text,
value: kitchenItems[i].value
};
}
return item;
});
setkitchenItems(updatedData);
}, [kitchenItems]);
return (
<main>
{kitchenItems.map((item, i) => (
<div className="utensil" key={i}>
<h2>{item.name}</h2>
<ul>
<li>{item.size}</li>
<li>{item.text}</li>
<li>{item.value}</li>
</ul>
<button type="button" name="apply" onClick={() => applyAll(i)}>
Apply to All
</button>
</div>
))}
</main>
);
}
export default App;
If I understand what you are trying to accomplish, this should solve your issue.
setkitchenItems() is the asynchronous method, and you can't get the updated value of kitchenItems immediately after setkitchenItems().
setkitchenItems(updatedData);
console.log(kitchenItems); // This will console the old `kitchenItems`
You should use useEffect with adding kitchenItems dependency to check updated state value.
useEffect(() => {
console.log(kitchenItems);
}, [kitchenItems]);

Categories