Displaying Nested Objects in React after fetching the data from an api? - javascript

The user data from json placeholder has the address as an object inside of the user object and I am trying to figure out how to display that data for learning and understanding purposes
import React, { useState, useEffect } from "react";
import "./App.css";
const App = () => {
const [users, setUsers] = useState([]);
const [user, setUser] = useState({});
useEffect(() => {
getUsers();
getUser();
//eslint-disable-next-line
}, []);
const getUsers = async () => {
const res = await fetch("https://jsonplaceholder.typicode.com/users");
const data = await res.json();
setUsers(data);
};
const getUser = async () => {
const res = await fetch("https://jsonplaceholder.typicode.com/users/1");
const data = await res.json();
setUser(data);
console.log(data);
};
return (
<div className="App">
<h3 style={{ marginBottom: "5px" }}>
Getting an Array of users from an Api
</h3>
<ul>
{users.map(user => (
<li style={{ marginBottom: "5px", paddingLeft: "10px" }}>
Name:{user.name} <br />
Email:{user.email}
</li>
))}
</ul>
<div className="card" style={{ width: "400px" }}>
<h3>Name: {user.name}</h3>
<p>Email: {user.email}</p>
</div>
</div>
);
};
export default App;
to this point everything works just fine
when I try to access the user address it throws an error
<div className="card" style={{ width: "400px" }}>
<h3>Name: {user.name}</h3>
<p>Email: {user.email}</p>
<p>Address: {user.address.street}
</div>
How do I access and object within an object?

You have to set init user.
Because of rendering null issue.
const defaultUser = {
name: "",
email: "",
address: {
street: ""
}
};
const [users, setUsers] = useState([defaultUser]);
const [user, setUser] = useState(defaultUser);

Related

i am not getting displayed the post

in this i have put request the api its being consoled i want to show it on my screen like crud can anyone help me with it i have used axios.put i think i havnt added the code plaese help me.please help me with this i am a beginner in react.
my api link [restapi]
i am posting my code bekow please go through it:
import axios from "axios";
import react, { useEffect, useState } from "react";
import './App.css'
export default function App() {
const [users, setUsers] = useState([]);
const [searchTerm, setsearchTerm] = useState("");
const [title, setTitle] = useState('');
const [body, setBody] = useState('');
const postData =(e)=>{
e.preventDefault();
axios.post("https://reqres.in/api/users?page=1",{
title,
body
} ).then(res=>console.log('posting',res))
}
useEffect(() => {
axios.get("https://reqres.in/api/users?page=1").then((res) => {
setUsers(res.data);
});
}, []);
return (
<div className="App">
<div className="flex">
<input
onChange={(e) => {
setsearchTerm(e.target.value);
} }
type="text"
placeholder="search"
className="form-control"
style={{
width: "80%",
borderRadius: "30px 0 30px",
justifyContent: "center",
marginLeft: "100px",
}} />
<br/>
<br/>
<form>
<label>first_name</label>
<input value={title} onChange={(e)=>setTitle(e.target.value)}/>
<label>email</label>
<input value={body} onChange={(e)=>setBody(e.target.value)}/>
<button onClick={postData}>post</button>
{users?.data?.filter((val) => {
if (searchTerm === "") {
return val;
} else if (val.first_name.toLowerCase().includes(searchTerm.toLowerCase())) {
return val;
}
}).map((datas) => (
<div key={datas.id}>
<p>
<strong>{datas.first_name}</strong>
</p>
<p>{datas.email}</p>
<img key={datas.avatar} src={datas.avatar} />
</div>
))}
</form>
</div>
</div>
);
}
Try the below code. I hope that's how you wanted it to work.
Post request to api - https://reqres.in/api/users?page=1 return data in different form than the api - https://reqres.in/api/users?page=1. So in order to add and show the entered data in the users list, you have to customized the data before adding it to the users useState. Like this:
setUsers([
...users,
{
id: res.data.id,
first_name: res.data.title,
email: res.data.body,
avatar: "https://reqres.in/img/faces/5-image.jpg",
},
]);
Note - I have used a random image while adding the entered data to the users list. As no avatar was being returned from the api.
Full working code:
import axios from "axios";
import react, { useEffect, useState } from "react";
import "./App.css";
export default function App() {
const [users, setUsers] = useState([]);
const [searchTerm, setsearchTerm] = useState("");
const [title, setTitle] = useState("");
const [body, setBody] = useState("");
const postData = (e) => {
e.preventDefault();
axios
.post("https://reqres.in/api/users?page=1", {
title,
body,
})
.then((res) => {
setUsers([
...users,
{
id: res.data.id,
first_name: res.data.title,
email: res.data.body,
avatar: "https://reqres.in/img/faces/5-image.jpg",
},
]);
});
};
useEffect(() => {
axios.get("https://reqres.in/api/users?page=1").then((res) => {
setUsers(res.data.data);
});
}, []);
return (
<div className="App">
<div className="flex">
<input
onChange={(e) => {
setsearchTerm(e.target.value);
}}
type="text"
placeholder="search"
className="form-control"
style={{
width: "80%",
borderRadius: "30px 0 30px",
justifyContent: "center",
marginLeft: "100px",
}}
/>
<br />
<br />
<form>
<label>first_name</label>
<input value={title} onChange={(e) => setTitle(e.target.value)} />
<label>email</label>
<input value={body} onChange={(e) => setBody(e.target.value)} />
<button onClick={postData}>post</button>
{users
.filter((val) => {
if (searchTerm === "") {
return val;
} else if (
val.first_name.toLowerCase().includes(searchTerm.toLowerCase())
) {
return val;
}
})
.map((datas) => (
<div key={datas.id}>
<p>
<strong>{datas.first_name}</strong>
</p>
<p>{datas.email}</p>
<img key={datas.avatar} src={datas.avatar} />
</div>
))}
</form>
</div>
</div>
);
}

How to display images from a JSON URL array in React

I have converted a JSON endpoint into a JavaScript array and I've mapped through it to get the key values I need. 3 out of 4 are text values but the first one is an image and it just displays the URL link. I have tried to map through the same array and display just the images and it works but then I cannot merge the two elements into one div.
The code:
export default function Pokes() {
const [pokemonData, setPokemonData] = React.useState({});
React.useEffect(() => {
fetch(
"https://raw.githubusercontent.com/Biuni/PokemonGO-Pokedex/master/pokedex.json"
)
.then((res) => res.json())
.then((data) => setPokemonData(data.pokemon));
}, []);
const allPokes = pokemonData;
const pokemons = Object.values(allPokes);
const pokesData = pokemons.map(pokemon => `${pokemon.img} ${pokemon.num} ${pokemon.name} ${pokemon.type}`);
let renderedOutput = pokesData.map(item => <div className="infodiv" style={{ flex: 1, flexBasis: "33%" }}> {item} </div>)
return (
<main>
<div>
<div style={{ display: "flex", flexWrap: "wrap" }}>{renderedOutput}</div>
</div>
</main>
);
}
const pokesData = pokemons.map(pokemon => `${pokemon.img} ${pokemon.num} ${pokemon.name} ${pokemon.type}`)
This line of code would return "image url number name", what you actually want is the real image which requires the use of the img HTML tag. Implementing this with your code, it would become:
export default function Pokes() {
const [pokemonData, setPokemonData] = React.useState({});
React.useEffect(() => {
fetch(
"https://raw.githubusercontent.com/Biuni/PokemonGO-Pokedex/master/pokedex.json"
)
.then((res) => res.json())
.then((data) => setPokemonData(data.pokemon));
}, []);
const allPokes = pokemonData;
const pokemons = Object.values(allPokes);
let renderedOutput = pokemons.map(pokemon => <div className="infodiv" style={{ flex: 1, flexBasis: "33%" }}> <img src={pokemon.img} /> {pokemon.num} {pokemon.name} </div>)
// Note the code change above ^^^
return (
<main>
<div>
<div style={{ display: "flex", flexWrap: "wrap" }}>{renderedOutput}</div>
</div>
</main>
);
}
Here is the solution if that is what you are looking after. Here is codesandbox for below code;
import { useState, useEffect, useCallback } from "react";
import axios from "axios";
const URI =
"https://raw.githubusercontent.com/Biuni/PokemonGO-Pokedex/master/pokedex.json";
const App = () => {
const [values, setValues] = useState([]);
const getPokomonGo = useCallback(async () => {
try {
const { data } = await axios.get(URI);
if (data) setValues(data?.pokemon);
} catch (err) {
console.log({ err });
}
}, []);
useEffect(() => {
getPokomonGo();
}, [getPokomonGo]);
return (
<div className="App">
<h1>Pokemon Images</h1>
{values &&
values.map(({ num, name, img }) => (
<img src={img} alt={name} key={num} />
))}
</div>
);
};
export default App;
<img src={{item.img}} alt="Lamp" width="100" height="100">

State not changing correctly after receiving props from HOC

I created a HOC to handle all the logic necessary for sockets setup + handlers and wrapped my component into it passing HOC's state at the same time. I added useEffect to the wrapped component to change it's state after it gets new props from HOC. The problem is that even if it logs these props correctly in the console, it is somehow broken. The output doesn't display even after getting props, and the loading spinner is working all the time despite the fact that the loading state is set to false from the beginning. Does anyone know what may be causing this and how can I fix this?
HOC:
import React, { useState, useEffect, useContext } from "react";
import SocketContext from "../../components/sockets/socketContext";
import axios from "axios";
import { SentimentOutput } from "./../../types/outputTypes";
import { TaskLoading } from "./../../types/loadingTypes";
export default function withSocketActions(HocComponent: any) {
return (props: any) => {
const [output, setOutput] = useState({
score: undefined,
label: undefined,
});
const [loading, setLoading] = useState(false);
const contextProps = useContext(SocketContext);
useEffect(() => {
if (contextProps) {
const { socket } = contextProps;
socket.on("status", (data: any) => {
if (
data.message.status === "processing" ||
data.message.status === "pending"
) {
setLoading(true);
console.log(data);
} else if (data.message.status === "finished") {
setLoading(false);
getOutput(data.message.task_id);
console.log(data);
}
});
return () => {
socket.off("");
};
}
}, []);
const getOutput = async (id: string) => {
const response = await axios.get(`http://localhost:9876/result/${id}`);
console.log("Output: ", response.data);
setOutput(response.data);
};
return (
<>
<HocComponent props={{ ...props, output, loading }} />
</>
);
};
}
Component:
import React, { useState, FormEvent, useEffect, useContext } from "react";
import axios from "axios";
import PulseLoader from "react-spinners/PulseLoader";
import { faTag, faPoll } from "#fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import withSocketActions from "../../components/sockets/withSocketActions";
import "../../styles/containers.scss";
import "../../styles/buttons.scss";
import "../../styles/text.scss";
function SentimentInput(props: any) {
const [input, setInput] = useState("");
const [output, setOutput] = useState({
score: "",
label: "",
});
const [loading, setLoading] = useState(false);
useEffect(() => {
setOutput({ score: props.output?.score, label: props.output?.label });
setLoading(props.loading);
console.log("OUTPUT: ", props);
}, [props]);
const getHighlightColour = (label: string | undefined) => {
if (label === "POSITIVE") return "#57A773";
else if (label === "NEGATIVE") return "#F42C04";
else return "transparent";
};
const submitInput = async (input: string) => {
let formData = new FormData();
formData.set("text", input);
if (props.model) formData.set("model", props.model);
const response = await axios.post(
`http://localhost:9876/run/sentiment_analysis`,
formData
);
console.log("RESPONSE: ", response.data.id);
};
const handleSubmit = async (e: FormEvent<HTMLButtonElement>) => {
e.preventDefault();
console.log(input);
const result = await submitInput(input);
};
return (
<div className="inputContainer">
<div style={{ width: "100%", height: "100%", justifyContent: "center" }}>
<textarea
value={input}
onChange={(e) => setInput(e.target.value)}
rows={25}
className={"inputArea"}
readOnly={loading}
style={{
boxShadow: `0 0 12px 2px ${getHighlightColour(
output && output.label
)}`,
}}
autoFocus
placeholder={"Insert text for evaluation"}
/>
<button
className={"submitInputButton"}
onClick={(e) => handleSubmit(e)}
>
<div className={"topButtonText"}>Evaluate</div>
</button>
<PulseLoader loading={loading} color={"white"} size={6} />
{output &&
output.score !== undefined &&
output.label !== undefined &&
!loading && (
<div
style={{
marginTop: "10px",
display: "flex",
justifyContent: "center",
}}
>
<FontAwesomeIcon
icon={faTag}
size={"lg"}
color={"#f0edee"}
style={{ paddingRight: "5px" }}
/>
<div
className={
output && output.label === "POSITIVE"
? "outputInfo labelPositive"
: "outputInfo labelNegative"
}
>
{output.label}
</div>
<FontAwesomeIcon
icon={faPoll}
size={"lg"}
color={"#f0edee"}
style={{ paddingRight: "5px" }}
/>
<div className={"outputInfo"}>{output.score}</div>
</div>
)}
</div>
</div>
);
}
export default withSocketActions(SentimentInput);
Writing #Drew's comment as an answer
<HocComponent props={{ ...props, output, loading }} />
looks to be nesting your props in a prop named props, i.e. props.props.output
change it to-
<HocComponent {...props} output={output} loading={loading} />

TypeError: Cannot read property 'data' of undefined for shopping cart functionality

I keep getting this error: TypeError: Cannot read property 'data' of undefined, when there is no data being passed to my shopping cart page. How can I fix this error? Ideally, I would just like the page to display: "This cart is empty". I tried adding a conditional statement above the UserCardBlock, but it did not change anything. Thank you
import React, { useState } from 'react'
import { useDispatch } from 'react-redux';
import {
removeCartItem,
onSuccessBuy
} from '../../../_actions/user_actions';
import UserCardBlock from './Sections/UserCardBlock';
import { Result, Empty, Button } from 'antd';
import Paypal from '../../utils/Paypal';
function CartPage(props) {
const dispatch = useDispatch();
console.log(props)
const [Total, setTotal] = useState(props.location.state.data.price)
const [ShowTotal, setShowTotal] = useState(true)
const [ShowSuccess, setShowSuccess] = useState(false)
const removeFromCart = (productId) => {
dispatch(removeCartItem(productId))
}
const transactionSuccess = (data) => {
dispatch(onSuccessBuy({
cartDetail: props.user.cartDetail,
paymentData: data
}))
.then(response => {
setShowSuccess(true)
setShowTotal(false)
}
)
}
const transactionError = () => {
console.log('Paypal error')
}
const transactionCanceled = () => {
console.log('Transaction canceled')
}
const propductList = (data) =>{
console.log(data)
setTotal(data)
}
return (
<div style={{ width: '85%', margin: '3rem auto' }}>
<h1>My Cart</h1>
<div>
<UserCardBlock
productData={props.location.state.data}
removeItem={removeFromCart}
productList={data => propductList(data)}
/>
{ShowTotal ? (
<div style={{ marginTop: "3rem" }}>
<h2>Total amount: ${Total * 15} </h2>
</div>
) : ShowSuccess ? (
<Result status="success" title="Successfully Purchased Items" />
) : (
<div
style={{
width: "100%",
display: "flex",
flexDirection: "column",
justifyContent: "center",
}}
>
<br />
<Empty description={false} />
<p>No Items In The Cart</p>
</div>
)}
</div>
{/* Paypal Button */}
{ShowTotal &&
<Paypal
toPay={Total}
onSuccess={transactionSuccess}
transactionError={transactionError}
transactionCanceled={transactionCanceled}
/>
}
</div>
)
}
export default CartPage
Seems like your component is dependent on location state.
const [Total, setTotal] = useState(props.location.state.data.price)
and
<UserCardBlock
productData={props.location.state.data}
Try using optional chaining and nullish coalescing
const [Total, setTotal] = useState(props.location?.state?.data?.price ?? 0)
<UserCardBlock
productData={props.location.state?.data ?? []}
It seems like you are using redux so i will suggest you to use redux store instead of location state.

Passing props to modal passes every object

I'm not 100% sure what's going on here. I've got a display component that displays a bunch of cards, using a map based on my database - On the card is an edit button that pops a modal up, passing props over to the edit form.. Here's kinda how it looks:
import React, { useState } from 'react'
import { useQuery, useMutation } from '#apollo/client'
import { GET_ALL_PROJECTS, REMOVE_PROJECT } from '../helpers/queries'
import { makeStyles } from '#material-ui/core/styles'
import DeleteIcon from '#material-ui/icons/Delete'
import EditIcon from '#material-ui/icons/Edit'
import AddForm from './AddForm'
import EditForm from './EditForm'
import AlertMessage from '../Alerts/AlertMessage'
import { Grid, Typography, Card, CardActionArea, CardActions, CardContent, CardMedia, Button, Modal, Backdrop, Fade } from '#material-ui/core'
const useStyles = makeStyles((theme) => ({
modal: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
},
paper: {
backgroundColor: theme.palette.background.paper,
border: '2px solid #000',
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3),
},
}));
const DisplayProjects = () => {
const styles = useStyles()
const [deleteItem] = useMutation(REMOVE_PROJECT)
const { loading, error, data } = useQuery(GET_ALL_PROJECTS)
const [status, setStatusBase] = useState('')
const [resultMessage, setResultMessage] = useState('')
const [addOpen, setAddOpen] = useState(false)
const [editOpen, setEditOpen] = useState(false)
const onDelete = (id, e) => {
e.preventDefault()
deleteItem({
variables: { id },
refetchQueries: [{ query: GET_ALL_PROJECTS }]
}).then(
res => handleSuccess(res),
err => handleError(err)
)
}
// Handles Result of the Delete Operation
const handleSuccess = (res) => {
console.log(res.data.deleteProject.proj_name)
// console.log('success!');
setResultMessage(res.data.deleteProject.proj_name)
setStatusBase({
msg: `Successfully Deleted ${resultMessage}`,
key: Math.random()
})
}
const handleError = (err) => {
console.log('error')
}
//Handles the Modal for Add Project
const handleAddOpen = () => {
setAddOpen(true);
};
const handleAddClose = () => {
setAddOpen(false);
};
//Handles the Modal for Edit Project
const handleEditOpen = () => {
setEditOpen(true);
};
const handleEditClose = () => {
setEditOpen(false);
};
if (loading) return '...Loading'
if (error) return `Error: ${error.message}`
return (
<div>
<div style={{ marginTop: 20, padding: 30 }}>
<Grid container spacing={8} justify='center' alignItems='center'>
{data.projects.map(p => {
return (
<Grid item key={p._id}>
<Card >
<CardActionArea>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<CardMedia
style={{ width: 400, height: 100, paddingTop: 10, }}
component='img'
alt='Project Image'
height='140'
image={require('../../images/html-css-javascript-lg.jpg')}
/>
</div>
<CardContent >
<Typography gutterBottom variant='h5' component="h2">
{p.proj_name}
</Typography>
<Typography component='p'>
{p.description}
</Typography>
</CardContent>
</CardActionArea>
<CardActions>
<Button>
<DeleteIcon onClick={e => onDelete(p._id, e)} />
</Button>
<Button onClick={handleEditOpen}>
<Modal
open={editOpen}
onClose={handleEditClose}
closeAfterTransition
BackdropComponent={Backdrop}
className={styles.modal}
>
<Fade in={editOpen}>
<div className={styles.paper}>
<EditForm
id={p._id}
close={handleEditClose}
name={p.proj_name}
desc={p.description}
gh={p.gh_link}
live={p.live_link}
img={p.image_url}
/>
</div>
</Fade>
</Modal>
<EditIcon />
</Button>
</CardActions>
</Card>
{ status ? <AlertMessage key={status.key} message={status.msg} /> : null}
</Grid>
)
}
)}
</Grid>
<Button type='button' onClick={handleAddOpen}>Add Project</Button>
<Modal
open={addOpen}
onClose={handleAddClose}
closeAfterTransition
BackdropComponent={Backdrop}
className={styles.modal}
>
<Fade in={addOpen}>
<div className={styles.paper}>
<AddForm close={handleAddClose} />
</div>
</Fade>
</Modal>
</div>
</div >
)
}
export default DisplayProjects
And here's the form. I've destructured out the props into variables and placed them into a state object called details, so they can be overwritten and submitted to the database..
import React, { useState } from 'react'
import { useParams } from 'react-router-dom'
import { useMutation, useQuery } from '#apollo/client'
import { EDIT_PROJECT, GET_ALL_PROJECTS, GET_PROJECT_BY_ID} from '../helpers/queries'
const AddForm = (props) => {
const params = useParams()
const id = params.toString()
// console.log(id);
const [editProjectItem] = useMutation(EDIT_PROJECT)
const {loading, data, error} = useQuery(GET_PROJECT_BY_ID, {
variables: {
id
},
})
const [details, setDetails] = useState({})
if (loading) return '...Loading';
if (error) return <p>ERROR: {error.message}</p>;
if (!data) return <p>Not found</p>;
setDetails(data.projectById)
console.log(data.projectById)
const submitForm = e => {
e.preventDefault()
try {
editProjectItem({
variables: { id, proj_name, description, gh_link, live_link, image_url},
refetchQueries: [{query: GET_ALL_PROJECTS}]
})
}
catch (err) {
console.log('You Goofed')
}
// setDetails({
// proj_name: '',
// description: '',
// gh_link: '',
// live_link: '',
// image_url: ''
// })
props.close()
}
const changeDetails = (e) => {
setDetails({
...details,
[e.target.name]: e.target.value
})
}
const {_id, proj_name, description, gh_link, live_link, image_url} = details
return (
<div key = {_id}>
<h2>Edit {proj_name}</h2>
<form onSubmit = {submitForm} >
<label>
Project Name:
<input
name = 'proj_name'
value = {proj_name}
onChange = {changeDetails}
/>
</label>
<label>Description</label>
<input
name = 'description'
value = {description}
onChange = {changeDetails}
/>
<label>GitHub Link</label>
<input
name = 'gh_link'
value = {gh_link}
onChange = {changeDetails}
/>
<label>Live Link</label>
<input
name = 'live_link'
value = {live_link}
onChange = {changeDetails}
/>
<label>Preview Image</label>
<input
name = 'image_url'
value = {image_url}
onChange = {changeDetails}
/>
<button type = 'submit'>Submit</button>
</form>
</div>
)
}
export default AddForm
The problem I'm running into, is that when I access the modal, the props are sent from literally EVERY Object, instead of the one, and displays the data for the last record instead of the one I want to edit You can see what happens here (I logged props.id in order to test) https://imgur.com/a/pcEKl89
What did I miss? (Disclaimer: I am still a student, and learning the craft.. be gentle on my code please)
EDIT: I just realized that I didn't indicate that this is the final form of the EditForm component. I haven't added the logic in to make the updates yet, I just wanted to get the data showing properly first.
EDIT2: I made some changes to how the ID is passed over, I was already using React-Router, so I went ahead and made a route to /edit/:id and then using useParams(), I got the ID that way. It seems to be working, however now I'm getting a Too many re-renders message. Updated the AddForm code above to reflect the changes..
I figured out the re-render issue.. it was as simple as dropping the setDetails function into a useEffect Hook:
useEffect(()=> {
if(data){
setDetails(data.projectById)
}
},[data])

Categories

Resources