I'm getting the error "TypeError: properties.map is not a function", however when i console.log(properties) i can see the data. Can anyone explain me what i'm doing wrong?
If i remove the map code obviously the error is not showing. Is it because "properties" is not defined yet during the render? I'm a bit confused of why it's not defined since as i said i can see the data on load with the console.
import React, { Component } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { AndroidOutlined, AppleOutlined } from '#ant-design/icons';
import Tabs, { TabPane } from '#iso/components/uielements/tabs';
import Select, { SelectOption } from '#iso/components/uielements/select';
import Button from '#iso/components/uielements/button';
import PageHeader from '#iso/components/utility/pageHeader';
import Box from '#iso/components/utility/box';
import { Form, Input, Checkbox, Col, Row } from 'antd'
import LayoutWrapper from '#iso/components/utility/layoutWrapper.js';
import ContentHolder from '#iso/components/utility/contentHolder';
import IntlMessages from '#iso/components/utility/intlMessages';
import FormGeneral from './FormGeneral';
import FormDetails from './FormDetails';
import propertiesActions from '#iso/redux/properties/actions';
const Option = SelectOption;
const {
loadFromApi,
} = propertiesActions;
export default function PropertiesPage() {
const dispatch = useDispatch();
React.useEffect(() => {
dispatch(loadFromApi());
}, [dispatch]);
const { properties, property, isLoading } = useSelector(
state => state.Properties
);
const rowStyle = {
width: '100%',
display: 'flex',
flexFlow: 'row wrap',
};
const colStyle = {
marginBottom: '16px',
};
const gutter = 16;
// Show data
console.log(properties);
console.log(isLoading);
return (
<LayoutWrapper>
<PageHeader>Properties</PageHeader>
<Box>
<Tabs defaultActiveKey="1">
<TabPane
tab={
<span>
<AppleOutlined />
General
</span>
}
key="1"
>
<div>
{console.log(properties)}
{properties.map(e =>
<div key={e.id}>
{e.id}
</div>
)}
</div>
<Row style={rowStyle} gutter={gutter} justify="start">
<Col md={12} sm={12} xs={24} style={colStyle}>
<Box>
<FormGeneral />
</Box>
</Col>
</Row>
</TabPane>
<TabPane
tab={
<span>
<AndroidOutlined />
Tab 2
</span>
}
key="2"
>
<FormDetails />
</TabPane>
</Tabs>
</Box>
</LayoutWrapper>
);
}
UPDATED:
properties is empty object at first
What if you try properties?.map, so it doesn't map if it is undefined, and wait until you really get the array. Because I don't have a big knowledge about redux, but from what I can read from your code, I understand that you are getting the properties asynchronously.
By default properties is undefined. .map cannot work with undefined. Try to use:
const { properties = [], property, isLoading } = useSelector(
state => state.Properties
);
It is possible to extend the answer and change the initialState, but your reducer code is needed.
before you map properties you should check if it is undefined because the initial value of state object in react is undefined and I think that if you will roll up in your console you should see an undefined print to the console
<div>
{console.log(properties)}
{properties !== undefined ? properties.map(e =>
<div key={e.id}>
{e.id}
</div>
): null}
</div>
In fact the object was defined but empty, i had to check for the length
<div>
{console.log(properties)}
{properties.length > 1 ? properties.map(e =>
<div key={e.id}>
{e.id}
</div>
): null}
</div>
Doing this resolved the issue.
Related
I'm trying to make a ecommerce site using the commerce.js library. I have already 8 products in the library API. I have a Products.js component where I'm passing those array of product objects I got from App.js and again passing single product object to the Product.js component. To do this, I'm using mapping function to pass each object as props to the Product.js component. And I'm receiving this error. I tried commenting out the mapping block and only console logged the products variable from App.js and the prop products in Products.js, there I'm getting all the 8 products so no problem in there. But I don't get it why I'm getting this error in the mapping function.
App.js
import React, { useState, useEffect } from "react";
import { commerce } from "./lib/commerce";
import { Products, Navbar } from "./components";
function App() {
const [products, setProducts] = useState([]);
const fetchProduct = async () => {
const data = await commerce.products.list();
setProducts(data);
};
useEffect(() => {
fetchProduct();
}, []);
console.log("products", products);
return (
<div className="App">
<Navbar />
<Products products={products} />
</div>
);
}
export default App;
Products.js
import React from "react";
import { Grid } from "#material-ui/core";
import Product from "./Product/Product";
import useStyles from "./style";
const Products = ({ products }) => {
const classes = useStyles();
return (
<main className={classes.content}>
<div className={classes.toolbar} />
<Grid container justifyContent="center" spacing={4}>
{products.data.map((product) => (
<Grid item key={product.id} xs={12} sm={6} md={4} lg={3}>
<Product product={product} />
</Grid>
))}
</Grid>
</main>
);
};
export default Products;
Product.js
import React from "react";
import {
Card,
CardMedia,
CardContent,
CardActions,
Typography,
IconButton,
} from "#material-ui/core";
import { AddShoppingCart } from "#material-ui/icons";
import useStyles from "./styles";
const Product = ({ product }) => {
const classes = useStyles();
return (
<Card className={classes.root}>
<CardMedia
className={classes.media}
image={product.media.source}
title={product.name}
/>
<CardContent>
<div className={classes.cardContent}>
<Typography variant="h5" gutterBottom>
{product.name}
</Typography>
<Typography variant="h5">
{product.price.formatted_with_symbol}
</Typography>
</div>
<Typography
variant="body2"
dangerouslySetInnerHTML={{ __html: product.description }}
/>
</CardContent>
<CardActions disableSpacing className={classes.cardActions}>
<IconButton aria-label="Add to Cart">
<AddShoppingCart />
</IconButton>
</CardActions>
</Card>
);
};
export default Product;
In your App.js change your fetchProduct function as follows:
const fetchProduct = async = {
const products = await commerce.products.list();
setProducts(products.data);
}
and then in your Products.js map over the products instead of products.data,
{products.map((product) => (
<Grid item key={product.id} xs={12} sm={6} md={4} lg={3}>
<Product product={product} />
</Grid>
))}
EXPLANATION:
The initial State of products is an empty array, which is passed down to the products component, where you tried products.data.map() but products is an array (initially) and there is no data property on arrays, so it threw error.
There are several ways you can get around this, but if you are only using the products data from the response then you can go with the above approach, where you map over the products not products.data and setProducts() with the products array instead of products object.
Do you can to attached screen with error?
I think, you try to read data before is are loading, you can prevent this by add something like this (products.data || []).map for example.. or change default state in App.js on useState({data:[]})
I'm aware that this error may be quite common and have been answered several times before, but I couldn't find a solution.
My code always throws this error: ".map is not a function". I know that this happens because data is not an array. So I tried to solve this with .keys function but this throws the ".keys is not a function" error. I'm declaring const data in the parent component and want to use it in the child component.
I think my error depends on a false use of .keys. But after much Googling I'm still not one step further.
This is my Child-Code:
import React from "react";
import Card from 'react-bootstrap/Card';
import Col from 'react-bootstrap/Col';
import Row from 'react-bootstrap/Row';
import Container from 'react-bootstrap/Container';
import {Link} from 'react-router-dom'
const PostsRow = (data) => {
{return (
<Container>
<Row>
{data.keys(data).map((data) => {
console.log(data + "is mount")
return (
<Col className="col-6 col-md-6 col-lg-3 card">
<Link to={data.url}>
<Card className=" text-center ">
<Card.Img variant="top" src={data.imagesrc} />
<Card.Body>
<Card.Title>{data.title}</Card.Title>
</Card.Body>
</Card>
</Link>
</Col>
);
})}
</Row>
</Container>
);
};
export default PostsRow;
This is home.jsx (parent):
import React from "react";
import './route.css';
import PostsRow from "../components/Content/PostsRow";
const Home = () => {
const data = {
title: "Ersti",
imagesrc: "./490.jpg",
url: "/meineposts"
};
return (
<div>
<PostsRow data={data}/>
</div>
);
};
export default Home;
This is working fine as long as const data is declared in the PostsRow.jsx, but when I try to declare it in Home.jsx and use props the above error throws.
As data is an object. To get its keys, you should write: Object.keys(data).
And, you have a typo in props destructuring : it should be ({data}).
Your example data is simply an object, not an array, so you don't need to use map or Object.keys here, you can simply write:
<Col className="col-6 col-md-6 col-lg-3 card">
<Link to={data.url}>
<Card className="text-center">
<Card.Img variant="top" src={data.imagesrc} />
<Card.Body>
<Card.Title>{data.title}</Card.Title>
</Card.Body>
</Card>
</Link>
</Col>
PostsRow will be called with props and data is property of it. so you have to use it like
const PostsRow = ({data}) => {
And you've to convert your data to array like
const data = [{
title: "Ersti",
imagesrc: "./490.jpg",
url: "/meineposts"
}];
When I map the array directly, I don't face any error but if I pass the same array through props, I face the error that "Cannot read property 'map' of undefined"
brands array
const brands = [
{
key: 1,
Name: "Nike",
},
{
key: 2,
Name: "Adidas",
},
{
key: 3,
Name: "Apollo",
},
{
key: 4,
Name: "Puma",
},
];
export default brands;
Parent Component
import React from "react";
import { Button, Checkbox } from "#material-ui/core";
import SimpleAccordion from "./simpleAccordion";
import "./Sidebar.css";
import brands from "./BrandNames";
function App() {
console.log("I am in App component", brands);
return (
<React.Fragment>
<div className="sidenav">
<SimpleAccordion children="Categories" brands={brands} /> // here passing the props as brands
<hr style={{ color: "#999999" }} />
{/* <h1>Helooooooo</h1>
{brands.forEach((brand) => {
return <span>{brand.Name}</span>;
})} */}
{brands.map((brand, key) => {
return (
<div>
<h1>{brand.Name}</h1>
<h1>{brand.key}</h1>
</div>
);
})}
<h1>Helooooooo</h1>
<SimpleAccordion children="Brands" />
<hr style={{ color: "#999999" }} />
<SimpleAccordion children="Stores" />
<hr style={{ color: "#999999" }} />
<SimpleAccordion children="Price" />
</div>
</React.Fragment>
);
}
export default App;
Child Component
import React,{useState} from 'react';
import Accordion from '#material-ui/core/Accordion';
import AccordionSummary from '#material-ui/core/AccordionSummary';
import AccordionDetails from '#material-ui/core/AccordionDetails';
import Typography from '#material-ui/core/Typography';
import AddIcon from '#material-ui/icons/Add';
import CloseIcon from '#material-ui/icons/Close';
import {Checkbox} from '#material-ui/core';
import brands from './BrandNames';
export default function SimpleAccordion(props) {
const[iconName,setIcon]= useState(false);
function handleClick(){
setIcon(!iconName);
}
// shows data at first time but then becomes undefine
console.log("here", props.brands)
return (
<React.Fragment>
// Directly mapping the (imported)"brands" array, no error
{brands.map((brand, key) => {
return (
<div>
<h1>{brand.Name}</h1>
<h1>{brand.key}</h1>
</div>
);
})}
// mapping props.brands array and there are errors.
{props.brands.map((brand, key) => {
return (
<div>
<h1>{brand.Name}</h1>
<h1>{brand.key}</h1>
</div>
);
})}
<Accordion style={{boxShadow:"0px 0px 0px "}} >
<AccordionSummary expandIcon={iconName ? <CloseIcon /> : <AddIcon/>} onClick={handleClick}>
<Typography style={{color:"#333333",fontWeight:"bolder"}}>{props.children}</Typography >
</AccordionSummary>
<AccordionDetails >
<Typography>
</Typography>
</AccordionDetails>
</Accordion>
</React.Fragment>
);
}
If I don't use the props in the child component, it works fine. brands.map works fine but when props are used, props.brands give the errors i.e TypeError: Cannot read property 'map' of undefined. Why are the props giving me the errors...
I was rendering more than one SimpleAccordion component but only passing props to the first component. That's why the props were undefined when mapped as other components were not given the props.
I have a react component that renders a forms with checkboxes and a chart (Victory). I want the component to show the active checkboxes in the graph (each checkboxes has its api url, for simplicity, the code below shows useless data and no fetching). There are multiple problems in this code:
1) The activestock state always seem to be one step behind in the point of view of the console.log, in the console it show the previous state instead of the actual content of the array. But in the React Dev Tools it shows the activestock correctly.
2) The VictoryChart doesnt get re-rendered even though its props (which is the activestock) changes, even though I can see in the React Dev Tools that multiple VictoryLines components exist. It's like the parents doesn't rerender on state changes, but I thought that by passing a state in props, you shared state between components?
3) Because the hooks rerenders the components, the checkboxes doesn't show a check when clicked.
Here is a sandbox link: https://codesandbox.io/s/crazy-murdock-3re1x?fontsize=14&hidenavigation=1&theme=dark
import React, {useState, useEffect}from 'react'
import Card from 'react-bootstrap/Card';
import Container from 'react-bootstrap/Container';
import Row from 'react-bootstrap/Row';
import { VictoryLine, VictoryChart } from 'victory';
export default function HomeCards(props){
const [activestock, setActiveStock] = useState([])
function handleActiveStocks(props){
if (activestock.includes(props) === false){
setActiveStock([...activestock, props])
console.log(activestock)
}
else {
setActiveStock(activestock.filter(activestock => activestock !== props))
console.log(activestock)
}
}
function Lines(props){
const charts = []
if (props.chart !== undefined){
props.chart.map(function(val, index){
charts.push(
<VictoryLine data={[
{x:index+1, y:index+2},
{x:index+2, y:index+4}
]}/>
)
})
return charts
}
return null
}
function RealCharts(props){
return(
<VictoryChart>
<Lines chart={props.stocks}></Lines>
</VictoryChart>
)
}
function Forms(props){
const hg=[]
props.text.map(function(val, index){
hg.push(
<form>
{val} <input type='checkbox' onClick={()=> handleActiveStocks(val)}/> {index}
</form>
)
})
return(hg)
}
return(
<Container fluid>
<Row style={{position:'relative', top:'2vh'}}>
<Card style={{width:props.width}}>
<Card.Header>U.S Equity</Card.Header>
<Forms
text={['S&P 500', 'NASDAQ',' DOW', 'Russel 1000', 'Large Cap', 'Small Cap', 'MSFT', 'FB']}
/>
</Card>
<Card>
{/* <VictoryChart >
<Lines chart={activestock}/>
</VictoryChart> */}
<RealCharts stocks={activestock}/>
</Card>
</Row>
</Container>
)
}
You need a checked={some_value_here} for checkbox to be checked.
import React, { useState, useEffect } from "react";
import Card from "react-bootstrap/Card";
import Container from "react-bootstrap/Container";
import Row from "react-bootstrap/Row";
import { VictoryLine, VictoryChart } from "victory";
export default function HomeCards(props) {
const [activestock, setActiveStock] = useState([]);
function handleActiveStocks(props) {
if (activestock.includes(props) === false) {
setActiveStock([...activestock, props]);
console.log(activestock);
} else {
setActiveStock(activestock.filter(activestock => activestock !== props));
console.log(activestock);
}
}
function Lines(props) {
const charts = [];
if (props.chart !== undefined) {
props.chart.map(function(val, index) {
charts.push(
<VictoryLine
data={[
{ x: index + 1, y: index + 2 },
{ x: index + 2, y: index + 4 }
]}
/>
);
});
return charts;
}
return null;
}
function RealCharts(props) {
return (
<VictoryChart>
<Lines chart={props.stocks} />
</VictoryChart>
);
}
// useEffect()
function Forms(props) {
const hg = [];
props.text.map(function(val, index) {
console.log(activestock)
// check the value exists in the activeStock
const checked=activestock.includes(val);
hg.push(
<form>
{val}{" "}
<input type="checkbox" checked={checked} onClick={() => handleActiveStocks(val)} />{" "}
{index}
</form>
);
});
return hg;
}
return (
<Container fluid>
<Row style={{ position: "relative", top: "2vh" }}>
<Card style={{ width: props.width }}>
<Card.Header>U.S Equity</Card.Header>
<Forms
text={[
"S&P 500",
"NASDAQ",
" DOW",
"Russel 1000",
"Large Cap",
"Small Cap",
"MSFT",
"FB"
]}
/>
</Card>
<Card>
{/* <VictoryChart >
<Lines chart={activestock}/>
</VictoryChart> */}
<RealCharts stocks={activestock} />
</Card>
</Row>
</Container>
);
}
The shared state is not wrong, you just need to add necessary check before return it.
I have built a Trello clone using ReactJS, where I have 4 columns called TODO, DOING, DONE and REJECTED, where I can add a card to any column.
In a file I am trying to map over card component and rendering properties from defined dummy data.
What I want to do?
I want to delete a specific card when it is clicked using the card id in some way.
What is the problem?
I do not understand how do I delete an entire card object when it matches a certain id.
My TaskboardList.js component :
import React from "react";
import TaskboardCard from "./TaskboardCard";
import TaskboardActionButton from "./TaskboardActionButton";
import { Droppable } from "react-beautiful-dnd";
const TaskboardList = ({ title, cards, listID }) => {
return (
<Droppable droppableId={String(listID)}>
{provided => (
<div
className="taskboardlist_container"
{...provided.droppableProps}
ref={provided.innerRef}
style={styles.container}
>
<div className="sub-heading">{title}</div>
{cards.map((card, index) => (
<TaskboardCard
key={card.id}
index={index}
text={card.text}
id={card.id}
/>
))}
<TaskboardActionButton listID={listID} />
{provided.placeholder}
</div>
)}
</Droppable>
);
};
const styles = {
container: {
backgroundColor: "#eee",
width: 300,
padding: "0.5rem",
marginRight: "1rem",
height: "100%"
}
};
export default TaskboardList;
My TaskboardCard.js component
import React from "react";
import Card from "#material-ui/core/Card";
import Typography from "#material-ui/core/Typography";
import CardContent from "#material-ui/core/CardContent";
import { Draggable } from "react-beautiful-dnd";
const TaskboardCard = ({ text, id, index, sample }) => {
return (
<Draggable draggableId={String(id)} index={index}>
{provided => (
<div
className="taskboard_container"
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<Card>
<CardContent>
<Typography style={{ fontSize: "1.5rem" }} gutterBottom>
{text}
</Typography>
</CardContent>
</Card>
</div>
)}
</Draggable>
);
};
export default TaskboardCard;
For further reference to more files, I am attaching my GitHub link. Please consider visiting.
Any help would be much appreciated.
https://github.com/abhinav-anshul/consensolabs
And here is the link for a live demo https://consensolab.abhinavanshul.com/
// src/actions/index.js
export const CONSTANTS = {
ADD_CARD: "ADD_CARD",
ADD_LIST: "ADD_LIST",
DRAG_HAPPENED: "DRAG_HAPPENED",
DELETE_CARD: "DELETE_CARD"
};
// src/actions/cardActions.js
export const deleteCard = cardId => ({
type: CONSTANTS.DELETE_CARD,
payload: { cardId }
});
// src/reducers/listReducers.js
const listReducer = (state = initialState, action) => {
// ...
case CONSTANTS.DELETE_CARD: {
return {
...state,
cards: state.cards.filter(({id}) => id !== action.payload.cardId)
}
}
Then from your delete button, you just need to call dispatch(deleteCard(cardId))
Edit: I've updated your code sandbox to implement this https://codesandbox.io/s/stupefied-golick-7z78k