reactjs with axios “Cannot read property 'map' of undefined`” - javascript

I was messing around using React, and I get this error while reading json from a local file localhost. I have checked other questions but have not gotten any result. i have install react dev tools from chrome app store and check error so i do not get cross origin error.
Here's the code:
class Content extends React.Component {
constructor(){
super();
this.state={
json: {
categories: []
}
};
}
componentWillMount(){
var _this = this;
var loc = window.location.pathname;
var dir = loc.substring(0, loc.lastIndexOf('/'));
this.serverRequest =
axios
.get(dir+"/data.json")
.then(function(result) {
// we got it!
_this.setState({
json:result
});
});
}
render() {
var title = <a>{this.state.json.title}</a>;
return (
<div>
<h2>Content</h2>
<h3>{title}</h3>
{this.state.json.categories.map(function(item) {
return (
<div key={item.categoryID} className="category">
<div> {item.categoryName} </div>
<div> {item.categoryDescridivtion} </div>
<div> {item.videosCount} </div>
</div>
);
})}
</div>
);
}
}
Here's the JSON:
{
"categories": [{
"categoryID": "294",
"parentID": "304",
"subjectID": "7",
"categoryName": "Apps and Side Dishes (Laura)",
"categoryDescription": "Learn to make amazing appetizers and side dishes with Laura in the Kitchen.",
"videosCount": "101",
"forumCategoryID": "163"
}, {
"categoryID": "285",
"parentID": "304",
"subjectID": "7",
"categoryName": "Side Dishes",
"categoryDescription": "Side dish recipes for salads, vegetables, sauces with Hilah cooking.",
"videosCount": "38",
"forumCategoryID": "163"
}, {
"categoryID": "337",
"parentID": "304",
"subjectID": "7",
"categoryName": "Side Dishes (bt)",
"categoryDescription": "Side dish recipes with Byron Talbott.",
"videosCount": "5",
"forumCategoryID": "163"
}, {
"categoryID": "301",
"parentID": "304",
"subjectID": "7",
"categoryName": "Side Dishes for Barbecue",
"categoryDescription": "Barbecue side dish recipes done on the grill by the BBQ Pit Boys!",
"videosCount": "43",
"forumCategoryID": "163"
}, {
"categoryID": "297",
"parentID": "304",
"subjectID": "7",
"categoryName": "Soups and Salads (Laura)",
"categoryDescription": "Looking for the perfect recipe to start your meal? Or are you looking to eat something on the lighter side? These are sure to have you covered!",
"videosCount": "70",
"forumCategoryID": "163"
}],
"title": "Title page"
}
here is the ouput from debug console regarding result from axios debug console:

Your screenshot from the console makes it clear why it isn't working: result doesn't have a categories property. It's result.data that has the categories, axios wraps the result in an envelope of sorts giving you information about the request (config, headers, status, etc.) and provides the actual data as data. So:
this.serverRequest =
axios
.get(dir+"/data.json")
.then(function(result) {
// we got it!
_this.setState({
json:result.data // ***
});
});
Example:
class Content extends React.Component {
constructor(){
super();
this.state={
json: {
categories: []
}
};
}
componentWillMount(){
var _this = this;
var loc = window.location.pathname;
var dir = loc.substring(0, loc.lastIndexOf('/'));
this.serverRequest =
axios
.get(dir+"/data.json")
.then(function(result) {
// we got it!
console.log(result); // So you can check it against your image
_this.setState({
json:result.data
});
});
}
render() {
var title = <a>{this.state.json.title}</a>;
return (
<div>
<h2>Content</h2>
<h3>{title}</h3>
{this.state.json.categories.map(function(item) {
return (
<div key={item.categoryID} className="category">
<div> {item.categoryName} </div>
<div> {item.categoryDescridivtion} </div>
<div> {item.videosCount} </div>
</div>
);
})}
</div>
);
}
}
const data = {
"config": {
"some": "stuff"
},
data: {
"categories": [{
"categoryID": "294",
"parentID": "304",
"subjectID": "7",
"categoryName": "Apps and Side Dishes (Laura)",
"categoryDescription": "Learn to make amazing appetizers and side dishes with Laura in the Kitchen.",
"videosCount": "101",
"forumCategoryID": "163"
}, {
"categoryID": "285",
"parentID": "304",
"subjectID": "7",
"categoryName": "Side Dishes",
"categoryDescription": "Side dish recipes for salads, vegetables, sauces with Hilah cooking.",
"videosCount": "38",
"forumCategoryID": "163"
}, {
"categoryID": "337",
"parentID": "304",
"subjectID": "7",
"categoryName": "Side Dishes (bt)",
"categoryDescription": "Side dish recipes with Byron Talbott.",
"videosCount": "5",
"forumCategoryID": "163"
}, {
"categoryID": "301",
"parentID": "304",
"subjectID": "7",
"categoryName": "Side Dishes for Barbecue",
"categoryDescription": "Barbecue side dish recipes done on the grill by the BBQ Pit Boys!",
"videosCount": "43",
"forumCategoryID": "163"
}, {
"categoryID": "297",
"parentID": "304",
"subjectID": "7",
"categoryName": "Soups and Salads (Laura)",
"categoryDescription": "Looking for the perfect recipe to start your meal? Or are you looking to eat something on the lighter side? These are sure to have you covered!",
"videosCount": "70",
"forumCategoryID": "163"
}],
"title": "Title page"
},
"headers": {
"some": "stuff"
}
};
const axios = {
get() {
return new Promise(resolve => {
setTimeout(resolve, 100, data);
});
}
};
ReactDOM.render(
<Content />,
document.getElementById("react")
);
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

Related

"TypeError: Cannot read property 'filter' of undefined" and similarly for other array functions like map using ReactJS and redux

I am following a tutorial and when at this point I'm getting stuck. It gives me TypeError: Cannot read property 'filter' of undefined when I try to filter through the Product array. I was implementing the same functionality using Axios according to the tutorial and it was working. The instructor then changed to redux and I followed the same steps and that's when I got the error. I'm pretty new to React and completely new to Axios and Redux and I've been trying to find out what's wrong but I'm getting nowhere.
Here's my ProductScreens.jsx where the error occurs:
import React, { useEffect } from "react";
import Rating from "../Components/rating.jsx";
import { useDispatch, useSelector } from "react-redux";
import { listProducts } from "../actions/productActions.js"
function Sale({product}){
if("salePrice" in product){
return <li>Sale Price: ₹{product.salePrice}</li>;
}
else
return null;
}
export default function ProductsScreen(props){
const dispatch = useDispatch();
const productList = useSelector( state => state.productList);
const {loading,error,products} = productList;
useEffect(() =>{
dispatch(listProducts());
},[dispatch]);
const product = products.filter(prod => prod.category === props.match.params.category && prod.subcategory === props.match.params.subcategory);
return (
<div className="container-fluid main-cards">
<div className="row">
{
product.map(product => (
<div key={product._id} className="col-12 col-md-6 col-lg-4 main-card-item">
<div className="card">
</img>
<div className="card-body product-card list-group">
<h5 className="card-title">{product.name}</h5>
<p className="card-text">{product.description}</p>
<ul className="list-unstyled mt-3 mb-4">
<li>Price: ₹{product.price}</li>
<Sale product={product}/>
<li>Buy Now</li>
<li>
<Rating rating={product.rating} numReview={product.numReview}/>
</li>
</ul>
</div>
</div>
</div>
))};
</div>
</div>
)
}
Here is my productActions.js
import Axios from "axios";
import { PRODUCT_LIST_REQUEST, PRODUCT_LIST_SUCCESS, PRODUCT_LIST_FAIL } from "../constants/productConstants"
export const listProducts = () => async (dispatch) =>{
dispatch({
type: PRODUCT_LIST_REQUEST,
});
try {
const {data} = await Axios.get('/api/products');
dispatch({type: PRODUCT_LIST_SUCCESS, payload : data});
}catch(error){
dispatch({type: PRODUCT_LIST_FAIL, payload: error.message});
}
}
Here's my productReducers.js
export const productListReducer = (state = {loading: true, products: [] }, action) =>{
switch(action.type){
case PRODUCT_LIST_REQUEST :
return {loading: true};
case PRODUCT_LIST_SUCCESS:
return {loading: false, products: action.payload};
case PRODUCT_LIST_FAIL :
return {loading: false, error: action.payload};
default:
return state;
}
}
And my productConstants.js
export const PRODUCT_LIST_REQUEST = "PRODUCT_LIST_REQUEST";
export const PRODUCT_LIST_SUCCESS = "PRODUCT_LIST_SUCCESS";
export const PRODUCT_LIST_FAIL = "PRODUCT_LIST_FAIL";
I've tried solving it but I can't find out what's wrong. I did console.log(products) instead of const product = products.filter(prod => prod.category === props.match.params.category && prod.subcategory === props.match.params.subcategory); in ProductScreens.jsx and also replaced the contents of return with <h1></h1> and got back the following object:
[
{
"_id": "1",
"name": "example1",
"category": "example category1",
"subcategory": "example subcategory1",
"image": [
{
"_id": "image1",
"name": "/images/example1.jpg"
}
],
"mainImage": "/images/example1.jpg",
"price": "19000",
"brand": "brand1",
"rating": 4.5,
"numReview": 10,
"description": "some description for example1."
},
{
"_id": "2",
"name": "example2",
"category": "example category1",
"subcategory": "example subcategory1",
"image": [
{
"_id": "image2",
"name": "/images/example2.jpg"
}
],
"mainImage": "/images/example2.jpg",
"price": "16791",
"salePrice": "15500",
"brand": "brand2",
"rating": 4.7,
"numReview": 10,
"description": "some description for example2."
},
{
"_id": "3",
"name": "example",
"category": "example category2",
"subcategory": "example subcategory3",
"image": [
{
"_id": "image3",
"name": "/images/example3-1.jpg"
},
{
"_id": "image4",
"name": "/images/example3-2.jpg"
},
{
"_id": "image5",
"name": "/images/example3-3.jpg"
},
{
"_id": "image6",
"name": "/images/example3-4.jpg"
}
],
"mainImage": "/images/example3-1.jpg",
"price": "8549",
"salePrice": "7200",
"brand": "brand3",
"rating": 3,
"numReview": 10,
"description": "some description for example3."
},
{
"_id": "4",
"name": "example4",
"category": "example category3",
"subcategory": "example subcategory4",
"image": [
{
"_id": "image7",
"name": "/images/example4.jpg"
}
],
"mainImage": "/images/example4.jpg",
"price": "450",
"brand": "brand4",
"rating": 4.5,
"numReview": 10,
"description": "some description for example4."
},
{
"_id": "5",
"name": "example5",
"category": "example category1",
"subcategory": "example subcategory2",
"image": [
{
"_id": "image8",
"name": "/images/example5.jpg"
}
],
"mainImage": "/images/example5.jpg",
"price": "30000",
"salePrice": "27000",
"brand": "brand5",
"rating": 4.5,
"numReview": 10,
"description": "some description for example5"
}
]
Looking at the object above, filter() should work but it's not. Moreover, the above object was the same object I got when I used axios and it worked fine then. So I have no idea why it's not working anymore.
Also when I try to console.log(products._id) or any other property, I get the same TypeError. I tried console.log(products[0]) thinking maybe I have to do that to get the first object since its an array of objects, I got TypeError: Cannot read property '0' of undefined.
So I figured it out. I found two solutions. First, Turns out I was getting two objects before the PRODUCT_LIST_SUCCESS action occured. In the first object, the products was an empty array from the initial state and in the second object which was receieved from PRODUCT_LIST_REQUEST action, there was only a loading property so products was undefined. So I changed the switch case statement in productsReducers.js for PRODUCT_LIST_REQUEST to return {loading: true, products: []}; and that did the trick.
My second solution is to check if loading is true or false and only execute my code and render the page if loading is false.
I haven't been an avid programmer so I don't know best programming practices but I'm using the second solution.

Get value based on sub-array unique value

My JSON data looks like this:
{
"data": [{
"id": "1",
"blogTitle": "How to plant a tree?",
"categories": [{
"CategoryID": "10",
"CategoryTitle": "Apple Tree"
}, {
"CategoryID": "11",
"CategoryTitle": "Mango Tree"
}, {
"CategoryID": "42",
"CategoryTitle": "Banana Tree"
}]
},
{
"id": "2",
"blogTitle": "How to make Juice?",
"categories": [{
"CategoryID": "71",
"CategoryTitle": "Apple Juice"
}, {
"CategoryID": "72",
"CategoryTitle": "Mango Juice"
}, {
"CategoryID": "73",
"CategoryTitle": "Banana Juice"
}]
}
]
}
What I want is to get the value of id by passing the value of CategoryID
For example: if I send 10 then I should get 1 in return because "CategoryID": "10" is in the same block with "id": "1"
Each CategoryID is unique resulting in a unique value of id
What did I do?
Closest I came up with was using the array.filter() but I am able to filter id value give the value of id but how do I get the value of id, given CategoryID
Could someone please kindly help me?
You can integrate your data, then use Array#some like this.
const yourObject = {"data":[{"id":"1","blogTitle":"How to plant a tree?","categories":[{"CategoryID":"10","CategoryTitle":"Apple Tree"},{"CategoryID":"11","CategoryTitle":"Mango Tree"},{"CategoryID":"42","CategoryTitle":"Banana Tree"}]},{"id":"2","blogTitle":"How to make Juice?","categories":[{"CategoryID":"71","CategoryTitle":"Apple Juice"},{"CategoryID":"72","CategoryTitle":"Mango Juice"},{"CategoryID":"73","CategoryTitle":"Banana Juice"}]}]};
const find_ID_BasedOn_GivenCategoryId = (categoryId) => {
for(const item of yourObject.data){
if(item.categories && item.categories.some(r => r.CategoryID == categoryId))
return item.id;
}
return "Not found";
}
console.log(find_ID_BasedOn_GivenCategoryId(11));
console.log(find_ID_BasedOn_GivenCategoryId(71));
console.log(find_ID_BasedOn_GivenCategoryId(999));
Simpler approach: using Array#find
const yourObject = {"data":[{"id":"1","blogTitle":"How to plant a tree?","categories":[{"CategoryID":"10","CategoryTitle":"Apple Tree"},{"CategoryID":"11","CategoryTitle":"Mango Tree"},{"CategoryID":"42","CategoryTitle":"Banana Tree"}]},{"id":"2","blogTitle":"How to make Juice?","categories":[{"CategoryID":"71","CategoryTitle":"Apple Juice"},{"CategoryID":"72","CategoryTitle":"Mango Juice"},{"CategoryID":"73","CategoryTitle":"Banana Juice"}]}]};
const find_ID_BasedOn_GivenCategoryId = (categoryId) => {
const result = yourObject.data.find(item =>
item.categories && item.categories.some(r => r.CategoryID == categoryId));
return result ? result.id : "Not found";
}
console.log(find_ID_BasedOn_GivenCategoryId(11));
console.log(find_ID_BasedOn_GivenCategoryId(71));
console.log(find_ID_BasedOn_GivenCategoryId(999));
You can use find with a combination of some for checking the categoryID that you want, for example:
const data = {
"data": [{
"id": "1",
"blogTitle": "How to plant a tree?",
"categories": [{
"CategoryID": "10",
"CategoryTitle": "Apple Tree"
}, {
"CategoryID": "11",
"CategoryTitle": "Mango Tree"
}, {
"CategoryID": "42",
"CategoryTitle": "Banana Tree"
}]
},
{
"id": "2",
"blogTitle": "How to make Juice?",
"categories": [{
"CategoryID": "71",
"CategoryTitle": "Apple Juice"
}, {
"CategoryID": "72",
"CategoryTitle": "Mango Juice"
}, {
"CategoryID": "73",
"CategoryTitle": "Banana Juice"
}]
}
]
};
const findId = (categoryID, { data }) => {
const foundItem = data.find(({ categories }) =>
categories.some(({ CategoryID }) => CategoryID == categoryID)
);
return foundItem ? foundItem.id : null;
}
console.log(findId(71, data))
find + some
var data = [
{
id: "1",
blogTitle: "How to plant a tree?",
categories: [
{
CategoryID: "10",
CategoryTitle: "Apple Tree"
},
{
CategoryID: "11",
CategoryTitle: "Mango Tree"
},
{
CategoryID: "42",
CategoryTitle: "Banana Tree"
}
]
},
{
id: "2",
blogTitle: "How to make Juice?",
categories: [
{
CategoryID: "71",
CategoryTitle: "Apple Juice"
},
{
CategoryID: "72",
CategoryTitle: "Mango Juice"
},
{
CategoryID: "73",
CategoryTitle: "Banana Juice"
}
]
}
];
const findId = (id) => {
return data?.find((item) =>
item?.categories?.some((i) => i?.CategoryID === id)
)?.id;
};
console.log(findId("10")); //1

How to a return an array of data after joining tables in Knex?

I have three tables I need to join: theaters, movies_theaters, movies. I need to return all data from the three tables but movies_theaters and movies are inside an array in theaters.
What I have so far:
const theatersMoviesJoin = knex('theaters as t')
.join('movies_theaters as mt', 't.theater_id', 'mt.theater_id')
.join('movies as m', 'm.movie_id', 'mt.movie_id');
const getAllTheaters = () => theatersMoviesJoin.select('t.*', 'm.*', 'mt.*');
The result looks like:
{
"theater_id": 1,
"name": "Regal City Center",
"address_line_1": "801 C St.",
"address_line_2": "",
"city": "Vancouver",
"state": "WA",
"zip": "98660",
"true": null,
"movie_id": 1,
"title": "Spirited Away",
"runtime_in_minutes": 125,
"rating": "PG",
"description": "Chihiro and her parents are moving to a small Japanese town in the countryside, much to Chihiro's dismay. On the way to their new home, Chihiro's father makes a wrong turn and drives down a lonely one-lane road which dead-ends in front of a tunnel. Her parents decide to stop the car and explore the area. They go through the tunnel and find an abandoned amusement park on the other side, with its own little town...",
"image_url": "https://imdb-api.com/images/original/MV5BMjlmZmI5MDctNDE2YS00YWE0LWE5ZWItZDBhYWQ0NTcxNWRhXkEyXkFqcGdeQXVyMTMxODk2OTU#._V1_Ratio0.6791_AL_.jpg",
"created_at": "2021-03-08T19:07:31.130Z",
"updated_at": "2021-03-08T19:07:31.130Z",
"is_showing": true
}
What I need it to look like:
{
"theater_id": 1,
"name": "Regal City Center",
"address_line_1": "801 C St.",
"address_line_2": "",
"city": "Vancouver",
"state": "WA",
"zip": "98660",
"created_at": "2021-02-23T20:48:13.335Z",
"updated_at": "2021-02-23T20:48:13.335Z",
"movies": [
{
"movie_id": 1,
"title": "Spirited Away",
"runtime_in_minutes": 125,
"rating": "PG",
"description": "Chihiro...",
"image_url": "https://imdb-api.com...",
"created_at": "2021-02-23T20:48:13.342Z",
"updated_at": "2021-02-23T20:48:13.342Z",
"is_showing": false,
"theater_id": 1
}
]
}
It can't be done with Knex (which is only a query builder), you will need to do it manually.
const flatList = [{ ... }, { ... }];
function groupByTheater(flatList) {
const groupedList = flatList.reduce((result, row) => {
result[row.theater_id] = result[row.theater_id] || {
theater_id: row.theater_id,
name: row.name,
address_line_1: row.address_line_1,
address_line_2: row.address_line_2,
city: row.city,
state: row.state,
zip: row.zip,
created_at: row.created_at,
updated_at: row.updated_at,
movies: [],
};
result[row.theater_id].movies.push({
movie_id: row.movie_id,
title: row.title,
runtime_in_minutes: row.runtime_in_minutes,
rating: row.rating,
description: row.description,
image_url: row.image_url,
created_at: row.created_at,
updated_at: row.updated_at,
is_showing: row.is_showing,
theater_id: row.theater_id,
});
return result;
}, {});
return Object.values(groupedList);
}
const groupedList = groupByTheater(flatList);

reactjs custom component input value doesnt update with state change

I am working on a list app, and I am having issues with the components not updating correctly. I pull the users list from a JSON file and save it in a state. I am using context to pass that state and other information around to my different compoents. The smallest component is the user items broken out into a list which is editable. It is here with the list of items that I am having issues with.
For example, I have two different JSON files:
[{"userId": 81944,
"listId": 1,
"title": "testa",
"items": [
{
"listItemId": 0,
"product": "walnuts",
"quantity": 1,
"category": "Bakery",
"unit": "Each",
"cart": false
},
{
"listItemId": 1,
"product": "syrup",
"quantity": 1,
"category": "Beverages",
"unit": "Each",
"cart": true
},
{
"listItemId": 2,
"product": "cinnamon",
"quantity": 6,
"category": "Bakery",
"unit": "Each",
"cart": false
},
{
"listItemId": 3,
"product": "gabonzo beans",
"quantity": 1,
"category": "Canned Goods",
"unit": "Each",
"cart": true
},
{
"listItemId": 4,
"product": "diced tomatos",
"quantity": 7,
"category": "Produce",
"unit": "Each",
"cart": false
},
{
"listItemId": 5,
"product": "milk",
"quantity": 1,
"category": "Dairy",
"unit": "Oz",
"cart": false
},
{
"listItemId": 6,
"product": "salmon",
"quantity": 3,
"category": "Meat",
"unit": "Lb",
"cart": false
}]},{
"userId": 78863,
"listId": 4,
"title": "testd",
"items": [
{
"listItemId": 0,
"product": "half and half",
"quantity": 1,
"category": "Dairy",
"unit": "Each",
"cart": false
},
{
"listItemId": 1,
"product": "Blue Cheese",
"quantity": 1,
"category": "Dairy",
"unit": "Each",
"cart": false
},
{
"listItemId": 2,
"product": "Garlic",
"quantity": 1,
"category": "Produce",
"unit": "Each",
"cart": false
},
{
"listItemId": 3,
"product": "Chestnuts",
"quantity": 1,
"category": "Other",
"unit": "Each",
"cart": false
},
{
"listItemId": 4,
"product": "Balsamic Vinegar",
"quantity": 1,
"category": "Other",
"unit": "Each",
"cart": false
},
{
"listItemId": 5,
"product": "Onions",
"quantity": 1,
"category": "Produce",
"unit": "Each",
"cart": false
},
{
"listItemId": 6,
"product": "Flax Seed",
"quantity": 1,
"category": "others",
"unit": "Each",
"cart": false
},
{
"listItemId": 7,
"product": "Plantains",
"quantity": 1,
"category": "Produce",
"unit": "Each",
"cart": false
}]}]
In my app I have a dialog box that allows me to switch between my lists. I then take list and pass it a custom component to be drawn on the screen.
import React, {useState,useEffect} from 'react';
const Card=(props)=>{
//console.log('prop');
const [cart, setCart] = useState(props.cart);
const [Product, setProduct] = useState(props.item);
const [Quantity, setQuantity] = useState(props.units);
// useEffect(()=>{
// setProduct(props.item)
// setQuantity(props.units)
// setCart(props.cart);
// },[])
console.log(props)
return (
<li key={props.value}>
<div>
<input type="checkbox" checked={cart} onChange={(e)=>{props.cartChange(e.target)}}/>
</div>
<div>
<input id={'product '+props.value} className='update'
type='text' value={Product}
onChange={(e)=>setProduct(e.target.value)}
/>
<br/>
<input id='quantityValue' className='update'
type='number' value={Quantity}
onChange={(e)=>setQuantity(e.target.value)}
/>
<span id='quantityType' className='update'>{props.unitType}</span>
</div>
<div>
<button id='save-button' type='button'
onClick={(e)=>{props.change(Product,Quantity,props.value)}}>✓ save</button>
<button id='delete-button' type='button'>✗ delete</button>
</div>
</li>
)
}
export default Card;
This is the code that calls the custom components. You will see that I am calling it from a array.map() those arrays are fine, and have the correct information in them.
import React, {useContext,useEffect} from 'react';
import {DataContext} from '../../../context/test/DataContext'
import Card from './ItemCard';
const update=(x)=>{
console.log(x)
}
const List = () =>{
const {listId} = useContext(DataContext);
const {userItemList} = useContext(DataContext);
const {GetItemList} = useContext(DataContext);
const {ListSplit} = useContext(DataContext);
const {foundList} = useContext(DataContext);
const {findList} = useContext(DataContext);
const {Updater} = useContext(DataContext);
const {cartUpdater} = useContext(DataContext);
useEffect(()=>{
GetItemList();
},[listId])
useEffect(()=>{
ListSplit();
},[userItemList])
// console.log(findList);
// console.log(foundList);
return(
<div>
<p>To find:</p>
<ul>
{findList.map((item,index)=><Card key={item.listItemId} index={index}
value={item.listItemId} cart={item.cart} item={item.product}
units={item.quantity} unitType={item.unit}
cartChange={cartUpdater} change={Updater} />)}
</ul>
<p>Found:</p>
<ul>
{foundList.map((item,index)=><Card key={item.listItemId} index={index}
value={item.listItemId} cart={item.cart} item={item.product}
units={item.quantity} unitType={item.unit}
cartChange={cartUpdater} change={Updater} />)}
</ul>
</div>
)
}
export default List;
Each time I switch this, the props that I console log out change correctly. Also, if I look at my compoents in dev tools (chrome) I see that the states should be correct, however what I see on the screen is not correct. For example the second item which is cinnamon, if I switch to the second list should be Blue Cheese. The prop changes, as does the state, but what I see on the screen is still cinnamon.
I know that I probably didnt explain it that clearly, but below is a screen shot of what I am talking about.
You were close with the commented out code. Since you are setting your props to state (which is a bad idea and I will discuss at the bottom), your useState only sets the state initially. You want to watch these props and update when they do.
useEffect(() => {
setProduct(props.item)
setQuantity(props.units)
setCart(props.cart);
}, [props.item, props.units, props.cart]);
The items in the array are what useEffect watches to know if it should fire.
As a side note - assigning props to state is a bad idea and you've seen why - they don't automatically update. You should hoist up where these props are set to the parent and you can pass them down as props and use them directly. You can pass down the setters as props as well, which can update the parent.
This article, while referencing class based React component may provide more information if you'd care to read up on it.
const { useState } = React;
const initialItems = [
{
listItemId: 1,
product: 'syrup',
quantity: 1,
category: 'Beverages',
unit: 'Each',
cart: true,
},
{
listItemId: 2,
product: 'cinnamon',
quantity: 6,
category: 'Bakery',
unit: 'Each',
cart: false,
},
{
listItemId: 3,
product: 'garbanzo beans',
quantity: 1,
category: 'Canned Goods',
unit: 'Each',
cart: true,
},
];
const Parent = () => {
const [items, setItems] = useState(initialItems);
const updateProduct = listItemId => (e) => {
setItems(items.map((item) => {
if (item.listItemId === listItemId) {
return { ...item, product: e.target.value };
}
return item;
}));
};
const updateQuantity = listItemId => (e) => {
setItems(items.map((item) => {
if (item.listItemId === listItemId) {
return { ...item, quantity: e.target.value };
}
return item;
}));
};
return (
<div>
<div style={{ width: "50%" }}>
All Items - (this state lives inside the parent component)
</div>
<div>
{items.map(item => (
<div>
<div>
Product - {item.product}
</div>
<div>
Quantity - {item.quantity}
</div>
</div>
))}
</div>
<div style={{ width: "50%" }}>
{items.map(item => (
<Child item={item} updateQuantity={updateQuantity} updateProduct={updateProduct} />
))}
</div>
</div>
);
};
const Child = ({ item, updateQuantity, updateProduct }) => {
return (
<div>
<div>
<span>
Product -
</span>
<span>
<input value={item.product} onChange={updateProduct(item.listItemId)} />
</span>
</div>
<div>
<span>
Quantity -
</span>
<span>
<input value={item.quantity} onChange={updateQuantity(item.listItemId)} />
</span>
</div>
</div>
);
};
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
A little example above. The parent component holds the state of the items and maps over each to create a child component. This example is a little rough around the edges. You could do something like adding data-id and name to each input to simplify the updating functions, or use useReducer to hoist that logic a bit, but I think it gets you going in the right direction.

React - Rendering an array of objects using Map

I'm trying to render an array of objects using Map and so far I've only been able to render the first item to the browser.
I figured something's up with my .map function, but I don't know enough about React and JS to pinpoint the problem.
Here's my App.js file:
// import stuff is here
class App extends Component {
constructor(props) {
super(props);
this.state = {
items: []
};
this.componentWillMount = this.componentWillMount.bind(this);
}
componentWillMount() {
fetch('THE-JSON-URL-IS-HERE')
.then(res => res.json())
.then(data => {
this.setState({ items: data });
});
render() {
const { items } = this.state;
return (
<div className="App">
{ items.map((item, num) => {
return (
<div className="people">
<div className="elem">
<p key={num}>{item.elems}</p>
</div>
<p key={num}><strong>{item.name}</strong></p>
<p key={num}><small>{item.title}</small></p>
<div className="hidden">
<p key={num}><small>{item.email}</small></p>
<p key={num}><small><strong>Office: </strong>{item.office}</small></p>
</div>
{/* <p>{item.manager}</p> */}
</div>
);
})}
</div>
);
}
}
export default App;
And here's a sample of the JSON file:
[
{
"elems": "Pr",
"name": "Abby Langdale",
"title": "President",
"email": "alangdale0#hubpages.com",
"office": "Javanrud",
"manager": [
{
"elems": "Vp",
"name": "Johnnie Mouncey",
"title": "Vice President",
"email": "jmouncey0#cnet.com",
"office": "Canto",
"manager": [
{
"elems": "Vp",
"name": "Concordia Burgwyn",
"title": "VP Quality Control",
"email": "cburgwyn0#dyndns.org",
"office": "Zhoukou",
"manager": [
{
"elems": "En",
"name": "Prissie Sainsberry",
"title": "Web Developer IV",
"email": "psainsberry0#yellowbook.com",
"office": "Tugu",
"manager": null
},
etc. Abby's info is all that I've rendered.
Since you're nesting arrays and objects into your first array element, the length of items is 1 and the only element is the Abby element with the rest of the data nested inside of it. To map through all of the elements, items should look like this array:
[
{
"elems": "Pr",
"name": "Abby Langdale",
"title": "President",
"email": "alangdale0#hubpages.com",
"office": "Javanrud",
"manager": ""
},
{
"elems": "Vp",
"name": "Johnnie Mouncey",
"title": "Vice President",
"email": "jmouncey0#cnet.com",
"office": "Canto",
"manager": ""
},
{
"elems": "Vp",
"name": "Concordia Burgwyn",
"title": "VP Quality Control",
"email": "cburgwyn0#dyndns.org",
"office": "Zhoukou",
"manager": ""
},
{
"elems": "En",
"name": "Prissie Sainsberry",
"title": "Web Developer IV",
"email": "psainsberry0#yellowbook.com",
"office": "Tugu",
"manager": null
}
]
If you need to maintain the relationship of managers, you can add an id to each object and reference it from another object.
[
{
"elems": "Pr",
"name": "Abby Langdale",
"title": "President",
"email": "alangdale0#hubpages.com",
"office": "Javanrud",
"manager": "",
"id" : 1
},
{
"elems": "Vp",
"name": "Johnnie Mouncey",
"title": "Vice President",
"email": "jmouncey0#cnet.com",
"office": "Canto",
"manager": 1
},
...
]
You would need a filter helper function to do the correct lookup for a manager's name but it should work.
Try flattening the array first. You would need to know the maximum number of levels that the array will have. Once it's flattened, you can use your map function:
const flatItems = items.flat(3); // flatten up to 3 levels
items.map((item, num) => {
return ( <render your div> );
}

Categories