Content not loading and logging with delay - javascript

I'm doing a React project for my bootcamp and I'm passing a details array as a descripcion props from my ItemDetailContainer component to my ItemDetail component. When I try to console.log my descripcion props in ItemDetail, it first logs an empty array and after a few seconds it logs the actual array with my product's details.
The problem is that the ItemDetails component is not rendering the data from the array but I can log its content.
I thought this was caused do to the setTimeOut function or UseEffect hook in my code but I still get this problem when I remove them
My ItemDetailContainer code:
import { useState, useEffect } from "react";
import Product from "../../product.json";
import ItemDetail from "./ItemDetail";
import { Link } from "react-router-dom";
const ItemDetailContainer = () => {
const [details, setDetails] = useState([]);
const getItem = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(Product)
}, 2000);
});
}
useEffect(() => {
getItem().then(setDetails)
}, []);
return (
<>
<div className="producto-descripcion">
<ItemDetail descripcion={details}></ItemDetail>
</div>
<button class="back-shopping"><Link className="route-link" to="/category/:id">go back to shopping</Link></button>
</>
)
}
export default `ItemDetailContainer`
My ItemDetail code:
import { useParams } from "react-router"
const ItemDetail = ({descripcion}) => {
console.log(descripcion)
const { id } = useParams();
return (
<>
<div className="produc-desc" id={id}>
{descripcion.filter(desc => desc.id === id).map((desc, index) => (
<div key={index}className="full-card">
<h2>{desc.name}</h2>
<img src={desc.pictureURL} alt="unisex hoodie" id="picture-store"/>
<p>{desc.price}</p>
<h4 id="texto-descripcion">{desc.description}</h4>
</div>
))}
</div>
</>
)
}
export default ItemDetail
The output of the console.log in the ItemDetail component:
Array(0)
and then:
(4) [{…}, {…}, {…}, {…}]
0: {id: 1, name: 'PHONE CASE', stock: '17', price: '$9.99', pictureURL: 'https://www.montblanc.com/variants/images/19971654706771425/A/w2500.jpg', …}
1: {id: 2, name: 'PINK HOODIE', stock: '12', price: '$24.99', pictureURL: 'https://cdn.shopify.com/s/files/1/0040/6146/2626/p…87992ClLogoHoodiepinkfront_1200x.png?v=1628299298', …}
2: {id: 3, name: 'WHITE SHIRT', stock: '23', price: '$14.99', pictureURL: 'https://hips.hearstapps.com/vader-prod.s3.amazonaw…m/1623333444-61jgr5y0ibl-ac-ul1500-1623333437.jpg', …}
3: {id: 4, name: 'BLACK HOODIE', stock: '9', price: '$24.99', pictureURL: 'https://ninefoldpath.org/wp-content/uploads/2018/05/NINE-BEATS-Logo-hoodie-600x600.jpg', …}
length: 4
What am I missing? Let me know if I wasnt clear. Thanks in advance.

The first time react renders the details state is [] therefore the first time it renders you see Array(0) in the console, and right after you see the desired array values, that is normal and expected React behaviour.
What you can do is something like this:
const ItemDetail = ({descripcion}) => {
console.log(descripcion)
const { id } = useParams();
return (
{
(description.length > 0) ?
<div className="produc-desc" id={id}>
{descripcion.filter(desc => desc.id === id).map((desc, index) => (
<div key={index}className="full-card">
<h2>{desc.name}</h2>
<img src={desc.pictureURL} alt="unisex hoodie" id="picture-store"/>
<p>{desc.price}</p>
<h4 id="texto-descripcion">{desc.description}</h4>
</div>
))}
</div>
: <div>Getting data</div>
}
)
}
export default ItemDetail
So you render a different message, in this case "Getting data" and as soon as the state changes you render the desired JSX, hope that makes sense.
There are many different ways to tackle this, I'm just giving you a simple workaround, you may want to check useEffect that will help you take more control over your component render lifecycles.

Finally solved it, basically the filter was comparing the useParams id which is a string to an integer id. Solved with a toString() like this:
Before desc.id === id after desc.id.toString() === id

Related

Calling dispatch function of useReducer (used in useContext ) causes the subscriber components to re-evaluate

The problem is that when I call the dispatch function of the useReducer hook declared in the Context SelectedMealContext , the<Item/> gets re-evaluated even if I am not changing the state of useReducer in the context.
My <MealLists/> component has array of objects and renders <Item/> by map() from mealsData .
The component tree is <MealLists/> > <Item/> > <ItemAmount/> > <Input/>
Only the <Item/> has called the Context as useContext(SelectedMealContext)
Note : <Input/> component is export default React.memo(Input) thats why it is re-evaluated only when <App/> loads .
When the app loads first the all of logged messages get shown ,Now on first click of Add button I get the re-evaluated messages again but after the first click and so on I don't get further re-evaluation as shown below in images:
The Console is cleared after App loaded and each call of dispatch function by click Add button
**
When app loads :
**
**
At first click :
**
**
At second click :
**
import Item from './item';
import Card from '../UI/card'
import styles from './mealists.module.css'
const mealsData = [
{
id: 'm1',
name: 'Sushi',
description: 'Finest fish and veggies',
price: 22.99,
},
{
id: 'm2',
name: 'Schnitzel',
description: 'A german specialty!',
price: 16.5,
},
{
id: 'm3',
name: 'Barbecue Burger',
description: 'American, raw, meaty',
price: 12.99,
},
{
id: 'm4',
name: 'Green Bowl',
description: 'Healthy...and green...',
price: 18.99,
},
]
const MealLists =()=>{
console.log("Meals Lists components");
return (
<>
<Card card={styles.meal} style={{marginTop: '200px'}}>
<ul >
{mealsData.map((mealItem)=>{
return <Item
key={mealItem.id}
id={mealItem.id}
mealName={mealItem.name}
mealDescription={mealItem.description}
mealPrice={mealItem.price}/>
})}
</ul>
</Card>
</>
)
}
export default MealLists;
My Context SelectedContext is as :
import React,{useReducer} from 'react'
export const SelectedMealContext = React.createContext();
const SelectedDishesReducer =(state,action)=>{
if(action.type ==='increment'){
console.log("Increment")
}else if(action.type === 'decrement'){
console.log("Decrement")
}else if(action.type === "new_meal"){
console.log("New Meal")
console.log(action.data)
}
}
const SelectedDishes = []
const SelectedMealContextProvider= (props) => {
console.log("Selected Meals Context evaluated");
const [SelectedMeals , dispatchAction ] = useReducer(SelectedDishesReducer,SelectedDishes);
console.log(SelectedMeals);
return (
<SelectedMealContext.Provider
value={{
SelectedMeals : SelectedMeals,
onIncrement : dispatchAction,
onDecrement : dispatchAction,
onAdd : dispatchAction
}}
>{props.children}
</SelectedMealContext.Provider>
)
}
export default SelectedMealContextProvider ;
and the subscriber component is <Item/>
import React ,{useContext} from 'react'
import styles from './item.module.css'
import ItemAmount from './ItemAmount'
import {SelectedMealContext} from '../DataContext/SelectedContext'
const Item =(props )=>{
console.log(`Item component for : ${props.id}`)
const Add = useContext(SelectedMealContext).onAdd;
const AddSelectedItems =(amount)=>{
console.table(props.id , props.mealName ,props.mealPrice)
let selectedDish = {
mealId: props.id,
mealName: props.mealName,
price: props.mealPrice,
number_of_meal : amount}
Add({type: "new_meal",data: selectedDish})
// console.log(`SelectedDish : ${selectedDish.number_of_meal}`)
}
return(
<li key={props.id} className={styles.meal}>
<div>
<h1 className={styles.mealName}>{props.mealName}</h1>
<h3 className={styles.description}>{props.mealDescription}</h3>
<h2 className={styles.price}>{props.mealPrice}</h2>
</div>
<ItemAmount AddSelectedItems={AddSelectedItems}/>
</li>
)
}
export default Item ;

Working with state on recursive components

I'm writing a component that renders itself inside recursively and is data-driven
Attaching my sandbox snippet, as it will be easier to see there.
This is my data:
var builderStructureData = [
{
id: 123,
value: 3,
children: []
},
{
id: 345,
value: 5,
children: [
{
id: 4123,
value: 34,
children: [
{
id: 342342,
value: 33,
children: []
}
]
},
{
id: 340235,
value: 3431,
children: [
{
id: 342231342,
value: 3415,
children: []
}
]
}
]
}
];
and it renders like this:
This is my App.js:
import { useState } from "react";
import "./App.css";
import Group from "./components/group";
import builderStructureData from "./components/builderStructureData";
function App() {
const [builderStructure, setBuilderStructure] = useState(
builderStructureData
);
return (
<div className="App">
{builderStructure.map((x) => {
return <Group key={x.id} children={x.children} value={x.value} />;
})}
</div>
);
}
export default App;
And this is my recursive component:
import React from "react";
export default function Group(props) {
let childrenArray = [];
if (props.children) {
props.children.map((x) => childrenArray.push(x));
}
return (
<div className="group" draggable>
<p>this is value: </p>
<input value={props.value} readOnly={true}></input>
<button>Add Group</button>
{childrenArray.map((x) => {
return <Group key={x.id} children={x.children} value={x.value} />;
})}
</div>
);
}
I can render the components based on the data, and it seems to be handling recursion fine. I need to store the state on the App.js page and be able to change it from within child components. For example, if I update the "value" field of the component with ID = 342342, I want it to update that corresponding object in the state no matter how deeply nested it is, but not sure how to do that as it is not as simple as just passing a prop.
Am I taking the right approach with my code snippet? How can I do the state update?
I would advise the state normalization approach - here is an example for redux state - https://redux.js.org/usage/structuring-reducers/normalizing-state-shape - but you can use this approach with your state. So - your state will look like this:
state = {
items: {
[123]: {
id: 123,
value: 3,
childrenIds: []
},
[345]: {
id: 345,
value: 5,
childrenIds: [4123, 340235]
},
[4123]: {
id: 4123,
value: 34,
parentId: 345,
childrenIds: [342342]
},
[342342]: {
id: 342342,
value: 33,
parentId: 4123,
childrenIds: []
},
[340235]: {
id: 340235,
value: 3431,
parentId: 345,
childrenIds: [342231342]
},
[342231342]: {
id: 342231342,
value: 3415,
parentId: 340235
childrenIds: []
}
}
}
Here the field "childrenIds" is an optional denormalization for ease of use, if you want - you can do without this field. With this approach, there will be no problem updating the state.
You are thinking this in a wrong way, it should be very easy to do what you want.
The most imported thing is to make a little small changes in Group
Please have a look
import React from "react";
export default function Group(props) {
const [item, setItem] = React.useState(props.item);
let childrenArray = [];
if (item.children) {
item.children.map((x) => childrenArray.push(x));
}
const updateValue = ()=> {
// this will update the value of the current object
// no matter how deep its recrusive is and the update will also happen in APP.js
// now you should also use datacontext in app.js togather with state if you want to
// trigger somethings in app.js
item.value =props.item.value= 15254525;
setState({...item}) // update the state now
}
return (
<div className="group" draggable>
<p>this is value: </p>
<input value={item.value} readOnly={true}></input>
<button>Add Group</button>
{childrenArray.map((x) => {
return <Group item={x} />;
})}
</div>
);
}
The code above should make you understand how easy it is to think about this as an object instead of keys.
Hop this should make it easy for you to understand

Pushing array with useState()

Everyone! i just have this kind of problem that i can`t fix.
this is my App.js
import { useState } from "react"
import Header from "./components/header"
import FeedbackList from "./components/FeedbackList"
import FeedbackData from "./data/FeedbackData"
function App() {
const [feedback, setFeedback] = useState(FeedbackData)
return (
<>
<Header />
<div className="container">
<FeedbackList feedback={feedback} />
</div>
</>
) }
export default App
and this is my second js file that i want to use "feedback" prop like array
import FeedbackItem from "./FeedbackItem"
import FeedbackData from "../data/FeedbackData"
function FeedbackList(feedback) {
return (
<div>
{feedback.map((item)=>(
<FeedbackItem key={item.id} item={item}/>
))}
</div>
)
}
i cant use feedback.map function in this case because feedback is not like array (sorry for my bad english) i solve this problem without using hooks but i want to know what i can do in this case,sorry if im writing something wrong im just new in React and trying to learn.
In Javascript there are object and array. Both can be mapped.
For Array.
const arr = [
{ name: "name 1", id: "01" },
{ name: "name 2", id: "02" },
{ name: "name 3", id: "03" },
];
arr.map(item=> (<div key={item.id}>{item.name}</div>))
For Object.
const obj = {
"item01": { name: "name 1" },
"item02": { name: "name 1" },
"item03": { name: "name 1" },
};
Object.keys(obj).map((key)=> <div key={key}>{obj[key].name}</div>)
Check your type, console.log(typeof FeedbackData).
If it is not type error. Instead of
const [feedback, setFeedback] = useState(FeedbackData)
Try this.
const [feedback,setFeedback] = useState([])
useEffect(()=> setFeedback(FeedbackData),[])
what you want is to destructure the prop you need from the 'props' object.
function Component1() {
const [val, setVal] = useState([]);
return <Component2 val={val} />
}
function Component2({ val }) {
return val.map...
}
this is equivalent to doing:
function Component2(props) {
return props.val.map...
}
this is because props is an object, and so you need to get the right key from the object based on the prop name, either by destructuring or accessing it via props.propName

Property 'map' of undefined in React

I'm learning a react course online. When I try to display the list of items from an array using map to display in a child component , I keep getting "cannot read property map of undefined.
Error is thrown while fetching data from users
import React, { Component } from "react";
import ReactDOM from "react-dom";
let userList = [
{ name: "John", age: 24, place: "India" },
{ name: "Henry", age: 24, place: "India" },
{ name: "Ulrich", age: 24, place: "India" }
];
const AppChild = ({ name, age, place, Graduated }) => {
return (
<section>
<p>name: {name}</p>
<p>age: {age}</p>
<p>place: {place}</p>
{/* access the value via props */}
<p>Graduated: {Graduated ? "yes!" : "no!"}</p>
</section>
);
};
export default class App extends Component {
state = {
userExists: true,
isGraduated: true,
loading: true,
};
toggleStatus = () => {
this.setState(prevState => ({
userExists: !prevState.userExists // value : false
}));
};
render() {
const { users } = this.props;
return (
<div>
<h2>Profile</h2>
<h4>
Profile Status is {this.state.userExists ? "Updated" : "Outdated"}
<br />
<button onClick={this.toggleStatus}>Check Status</button>
</h4>
{users.map(user => (
<AppChild
name={user.name}
age={user.age}
place={user.place}
Graduated={this.state.isGraduated} // passing state to child component
/>
))}
</div>
);
}
}
ReactDOM.render(<App users={userList} />, document.getElementById("root"));
To figure out the problem, we follow the bouncing ball. From the error message, I guess that the problem occurs on the line
{users.map(user => (
(You can confirm this from the stack trace given with the error message.)
The error tells you that users is undefined. So we look at the declaration for users:
const { users } = this.props;
Ok, so it is really this.props.users. So we look where this is passed in:
ReactDOM.render(<App users={userList} />, document.getElementById("root"));
Here you are passing the value of userList to a prop named users. However, in the code you show here, there is no variable named userList. This is as far as we can go with the information you have given. You need to find where this variable is declared and initialized to continue solving the problem.
Below is the correct code. In the previous code I was trying to render <App/> in both index.js and App.js. Thanks everyone for helping me out
=>index.js
import React from "react"
import ReactDOM from "react-dom"
import App from "./App"
let userList = [
{ name: "John", age: 24, place: "India" },
{ name: "Henry", age: 24, place: "India" },
{ name: "Ulrich", age: 24, place: "India" }
];
ReactDOM.render(<App users={userList} />, document.getElementById("root"));
=> App.js
import React, { Component } from "react";
// child component
const AppChild = ({ name, age, place, Graduated }) => {
return (
<section>
<p>name: {name}</p>
<p>age: {age}</p>
<p>place: {place}</p>
{/* access the value via props */}
<p>Graduated: {Graduated ? "yes!" : "no!"}</p>
</section>
);
};
// parent component
export default class App extends Component {
state = {
userExists: true,
isGraduated: true,
loading: true,
};
toggleStatus = () => {
this.setState(prevState => ({
userExists: !prevState.userExists // value : false
}));
};
render() {
const { users } = this.props;
return (
<div>
<h2>Profile</h2>
<h4>
Profile Status is {this.state.userExists ? "Updated" : "Outdated"}
<br />
<button onClick={this.toggleStatus}>Check Status</button>
</h4>
{users.map((user) => {
return(
<AppChild
name={user.name}
age={user.age}
place={user.place}
Graduated={this.state.isGraduated} // passing state to child component
/>
)})}
</div>
);
}
}
If you try to log users after following line of code
const { users } = this.props;
you'll see users is undefined.
Error message "cannot read property map of undefined" says the same thing, you can not apply map helper on an undefined variable. The map works with arrays

Creating HTML tag in react component

I'm not extending component class, trying to use usestate to manage state. Now I want to add a person component on certain conditions to personList variable inside the method togglePersonsHanler.
I'm expecting a list of HTML tags to be added like
<person name="person1" age=31>
<person name="person2" age=26>
<person name="person3" age=35>
but on console log, I'm getting personList as below
{$$typeof: Symbol(react.element), type: "div", key: null, ref: null, props: {…}, …}$$typeof: Symbol(react.element)type: "div"key: nullref: nullprops: {children: Array(3)}_owner: null_store: {validated: false}_self: null_source: {fileName: "D:\data\react\my-app\src\App.js", lineNumber: 72, columnNumber: 7}
and person tag is not getting added to DOM, any advice, please
import React, { useState } from 'react';
import './App.css';
import Person from './Person/Person';
const App = props => {
const [personState, setPersonState] = useState({
persons: [
{name: "person1", age:31},
{name: "person2", age:26},
{name: "person3", age:25}
],
other: "some Other Value"
} );
const [otherState,setOtherState]=useState({otherState :'some other value'});
const [showPersonsState,setShowPersonsState]=useState({showPersons :false});
let personList=null;
const togglePersonsHanler =() =>{
personList=null;
setShowPersonsState(
{showPersons : !showPersonsState.showPersons}
)
console.log(showPersonsState.showPersons);
if(showPersonsState.showPersons){
personList=(
<div>{personState.persons.map (person =>{
return <person name={person.name} age={person.age}/>
}
)}</div>
);
}
console.log(personList);
}
return (
<div className="App">
<h1> HI, I'm the react app</h1>
<button
//onClick={switchNameHandler.bind(this,'Gopu Ravi')}
onClick={togglePersonsHanler}
style={style}> Toggle Person </button>
{ personList }
</div>
);
}
export default App;
You're mapping the object literals by using them as an html tag. You likely meant to use the imported Person component.
<div>
{personState.persons.map (person => (
<Person name={person.name} age={person.age}/>
)}
</div>
And to fix a react-key warning since all mapped elements need unique keys, add a key prop with a value that is unique to the data in the array, like name:
<div>
{personState.persons.map (person => (
<Person key={name} name={person.name} age={person.age}/>
)}
</div>
To correctly toggle the display of the "personList":
Conditionally render the mapped persons array if showPersonsState is true
Simplify showPersonsState state to simply be the boolean value
Use functional state update to correctly toggle showPersonsState from previous state
Updated component code
const App = props => {
const [personState, setPersonState] = useState({
persons: [
{ name: "person1", age: 31 },
{ name: "person2", age: 26 },
{ name: "person3", age: 25 }
],
other: "some Other Value"
});
const [otherState, setOtherState] = useState({
otherState: "some other value"
});
const [showPersonsState, setShowPersonsState] = useState(false);
const togglePersonsHandler = () => setShowPersonsState(show => !show);
return (
<div className="App">
<h1> HI, I'm the react app</h1>
<button onClick={togglePersonsHandler}>Toggle Person</button>
{showPersonsState &&
personState.persons.map(({ age, name }) => (
<Person key={`${name}`} name={name} age={age} />
))}
</div>
);
};

Categories

Resources